
Type-safe client for interacting with Xtream Codes API, featuring DSL configuration, coroutine-friendly async calls, automatic retries, built-in HTTP caching, descriptive error handling and stream URL builders.
Xtream API is a modern, type-safe Kotlin Multiplatform library designed to seamlessly interact with Xtream Codes API. It enables effortless retrieval of movies, TV series, live TV streams, and more across Android, iOS, and JVM platforms.
Add the dependency to your build.gradle.kts:
dependencies {
implementation("io.github.saifullah-nurani:xtream-api:0.1.6")
}kotlin {
android()
ios()
jvm()
sourceSets {
commonMain.dependencies {
implementation("io.github.saifullah-nurani:xtream-api:0.1.6")
}
}
}<dependency>
<groupId>io.github.saifullah-nurani</groupId>
<artifactId>xtream-api</artifactId>
<version>0.1.6</version>
</dependency>import io.github.saifullah.xtream.Xtream
// Initialize the client
val xtream = Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
}// Authenticate and get user/server info
val authResult = xtream.auth()
println("Server: ${authResult.serverInfo.url}")
println("User: ${authResult.userInfo.username}")
println("Status: ${authResult.userInfo.status}")import android.app.Application
import io.github.saifullah.xtream.Xtream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
class MyApplication : Application() {
lateinit var xtream: Xtream
private set
override fun onCreate() {
super.onCreate()
xtream = Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
// Optional: Configure timeouts
socketTimeoutMillis = 30000
connectTimeoutMillis = 30000
requestTimeoutMillis = 30000
// Optional: Enable caching
useCache = true
// Optional: Configure retry
maxRetries = 3
retryDelayMillis = 1000
}
}
}import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.github.saifullah.xtream.model.XtreamMovie
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class MovieViewModel(private val xtream: Xtream) : ViewModel() {
private val _movies = MutableStateFlow<List<XtreamMovie>>(emptyList())
val movies: StateFlow<List<XtreamMovie>> = _movies
private val _loading = MutableStateFlow(false)
val loading: StateFlow<Boolean> = _loading
private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error
fun loadMovies() {
viewModelScope.launch {
_loading.value = true
_error.value = null
try {
val moviesList = xtream.movie.getMovies()
_movies.value = moviesList
} catch (e: Exception) {
_error.value = e.message ?: "Unknown error occurred"
} finally {
_loading.value = false
}
}
}
fun loadMovieDetail(streamId: Long) {
viewModelScope.launch {
try {
val movieDetail = xtream.movie.getMovieDetail(streamId)
// Handle movie detail
} catch (e: Exception) {
_error.value = e.message
}
}
}
}import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
private val viewModel: MovieViewModel by viewModels {
MovieViewModelFactory((application as MyApplication).xtream)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Observe movies
lifecycleScope.launch {
viewModel.movies.collect { movies ->
// Update UI with movies
updateMoviesList(movies)
}
}
// Observe loading state
lifecycleScope.launch {
viewModel.loading.collect { isLoading ->
// Show/hide loading indicator
showLoading(isLoading)
}
}
// Observe errors
lifecycleScope.launch {
viewModel.error.collect { error ->
error?.let {
showError(it)
}
}
}
// Load movies
viewModel.loadMovies()
}
private fun updateMoviesList(movies: List<XtreamMovie>) {
// Update RecyclerView or similar
}
private fun showLoading(isLoading: Boolean) {
// Show/hide progress bar
}
private fun showError(message: String) {
// Show error message
}
}// commonMain/src/commonMain/kotlin/XtreamClient.kt
import io.github.saifullah.xtream.Xtream
expect fun createXtreamClient(): Xtream
// androidMain/src/androidMain/kotlin/XtreamClient.android.kt
actual fun createXtreamClient(): Xtream {
return Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
}
}
// iosMain/src/iosMain/kotlin/XtreamClient.ios.kt
actual fun createXtreamClient(): Xtream {
return Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
}
}// Authenticate and get user/server information
val authResult = xtream.auth()
// Access server info
val serverUrl = authResult.serverInfo.url
val serverProtocol = authResult.serverInfo.serverProtocol
val timezone = authResult.serverInfo.timezone
// Access user info
val username = authResult.userInfo.username
val status = authResult.userInfo.status
val expDate = authResult.userInfo.expDate// Get all movies
val movies = xtream.movie.getMovies()
// Get movie detail
val movieDetail = xtream.movie.getMovieDetail(streamId = 123456)
// Get movie categories
val categories = xtream.movie.getMovieCategories()
// Get movies by category
val moviesInCategory = xtream.movie.getMoviesByCategory(categoryId = 10)// Get all TV series
val tvSeries = xtream.tvSeries.getTvSeries()
// Get TV series detail
val seriesDetail = xtream.tvSeries.getTvSeriesDetail(seriesId = 789012)
// Get TV series detail (alternative format)
val seriesDetail2 = xtream.tvSeries.getTvSeriesDetail2(seriesId = 789012)
// Get TV series categories
val categories = xtream.tvSeries.getTvSeriesCategories()
// Get TV series by category
val seriesInCategory = xtream.tvSeries.getTvSeriesByCategory(categoryId = 5)// Perform custom GET request
val response: MyCustomModel = xtream.custom.get {
url("https://api.example.com/custom-endpoint")
}
// Get raw HTTP response
val httpResponse = xtream.custom.getHttpResponse {
url("https://api.example.com/endpoint")
}
val statusCode = httpResponse.status.value
val body = httpResponse.bodyAsText()import io.github.saifullah.xtream.url.StreamUrlBuilder
import io.github.saifullah.xtream.model.XtreamAuthCredentials
// Build movie stream URL
val movieUrl = StreamUrlBuilder.buildMovieUrl(
protocol = "https",
host = "server.com",
username = "user",
password = "pass",
streamId = 123456,
extension = "mp4"
)
// Build episode stream URL
val episodeUrl = StreamUrlBuilder.buildEpisodeUrl(
protocol = "https",
host = "server.com",
username = "user",
password = "pass",
episodeId = 789012,
extension = "mkv"
)
// Using credentials object
val credentials = XtreamAuthCredentials(
protocol = "https",
host = "server.com",
port = 8080,
username = "user",
password = "pass"
)
val movieUrl = StreamUrlBuilder.buildMovieUrl(
authCredentials = credentials,
streamId = 123456,
extension = "mp4"
)val xtream = Xtream {
// Authentication
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
// Network timeouts (in milliseconds)
socketTimeoutMillis = 60000 // Default: 60000
connectTimeoutMillis = 60000 // Default: 60000
requestTimeoutMillis = 60000 // Default: 60000
useTimeout = true // Default: true
// HTTP settings
followRedirects = true // Default: true
expectSuccess = false // Default: false
// Caching
useCache = true // Default: true
// Retry configuration
maxRetries = 3 // Default: 0 (no retries)
retryDelayMillis = 1000 // Default: 1000
// Custom HTTP client (optional)
// httpClient(OkHttp) {
// // Custom configuration
// }
}import io.github.saifullah.xtream.model.XtreamAuthCredentials
// Use different credentials for a specific request
val customCredentials = XtreamAuthCredentials(
protocol = "https",
host = "other-server.com",
port = 8080,
username = "other_user",
password = "other_pass"
)
val movies = xtream.movie.getMovies(credentials = customCredentials)All API methods throw exceptions that should be handled:
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.ServerResponseException
import kotlinx.serialization.SerializationException
try {
val movies = xtream.movie.getMovies()
} catch (e: ClientRequestException) {
// HTTP 4xx errors
println("Client error: ${e.response.status}")
} catch (e: ServerResponseException) {
// HTTP 5xx errors
println("Server error: ${e.response.status}")
} catch (e: SerializationException) {
// JSON parsing errors
println("Serialization error: ${e.message}")
} catch (e: Exception) {
// Other errors
println("Error: ${e.message}")
}Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)This project is licensed under the MIT License - see the LICENSE file for details.
Saifullah Nurani
If you encounter any issues or have questions, please file an issue on the GitHub repository.
Made with β€οΈ using Kotlin Multiplatform
Xtream API is a modern, type-safe Kotlin Multiplatform library designed to seamlessly interact with Xtream Codes API. It enables effortless retrieval of movies, TV series, live TV streams, and more across Android, iOS, and JVM platforms.
Add the dependency to your build.gradle.kts:
dependencies {
implementation("io.github.saifullah-nurani:xtream-api:0.1.6")
}kotlin {
android()
ios()
jvm()
sourceSets {
commonMain.dependencies {
implementation("io.github.saifullah-nurani:xtream-api:0.1.6")
}
}
}<dependency>
<groupId>io.github.saifullah-nurani</groupId>
<artifactId>xtream-api</artifactId>
<version>0.1.6</version>
</dependency>import io.github.saifullah.xtream.Xtream
// Initialize the client
val xtream = Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
}// Authenticate and get user/server info
val authResult = xtream.auth()
println("Server: ${authResult.serverInfo.url}")
println("User: ${authResult.userInfo.username}")
println("Status: ${authResult.userInfo.status}")import android.app.Application
import io.github.saifullah.xtream.Xtream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
class MyApplication : Application() {
lateinit var xtream: Xtream
private set
override fun onCreate() {
super.onCreate()
xtream = Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
// Optional: Configure timeouts
socketTimeoutMillis = 30000
connectTimeoutMillis = 30000
requestTimeoutMillis = 30000
// Optional: Enable caching
useCache = true
// Optional: Configure retry
maxRetries = 3
retryDelayMillis = 1000
}
}
}import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.github.saifullah.xtream.model.XtreamMovie
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class MovieViewModel(private val xtream: Xtream) : ViewModel() {
private val _movies = MutableStateFlow<List<XtreamMovie>>(emptyList())
val movies: StateFlow<List<XtreamMovie>> = _movies
private val _loading = MutableStateFlow(false)
val loading: StateFlow<Boolean> = _loading
private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error
fun loadMovies() {
viewModelScope.launch {
_loading.value = true
_error.value = null
try {
val moviesList = xtream.movie.getMovies()
_movies.value = moviesList
} catch (e: Exception) {
_error.value = e.message ?: "Unknown error occurred"
} finally {
_loading.value = false
}
}
}
fun loadMovieDetail(streamId: Long) {
viewModelScope.launch {
try {
val movieDetail = xtream.movie.getMovieDetail(streamId)
// Handle movie detail
} catch (e: Exception) {
_error.value = e.message
}
}
}
}import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
private val viewModel: MovieViewModel by viewModels {
MovieViewModelFactory((application as MyApplication).xtream)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Observe movies
lifecycleScope.launch {
viewModel.movies.collect { movies ->
// Update UI with movies
updateMoviesList(movies)
}
}
// Observe loading state
lifecycleScope.launch {
viewModel.loading.collect { isLoading ->
// Show/hide loading indicator
showLoading(isLoading)
}
}
// Observe errors
lifecycleScope.launch {
viewModel.error.collect { error ->
error?.let {
showError(it)
}
}
}
// Load movies
viewModel.loadMovies()
}
private fun updateMoviesList(movies: List<XtreamMovie>) {
// Update RecyclerView or similar
}
private fun showLoading(isLoading: Boolean) {
// Show/hide progress bar
}
private fun showError(message: String) {
// Show error message
}
}// commonMain/src/commonMain/kotlin/XtreamClient.kt
import io.github.saifullah.xtream.Xtream
expect fun createXtreamClient(): Xtream
// androidMain/src/androidMain/kotlin/XtreamClient.android.kt
actual fun createXtreamClient(): Xtream {
return Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
}
}
// iosMain/src/iosMain/kotlin/XtreamClient.ios.kt
actual fun createXtreamClient(): Xtream {
return Xtream {
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
}
}// Authenticate and get user/server information
val authResult = xtream.auth()
// Access server info
val serverUrl = authResult.serverInfo.url
val serverProtocol = authResult.serverInfo.serverProtocol
val timezone = authResult.serverInfo.timezone
// Access user info
val username = authResult.userInfo.username
val status = authResult.userInfo.status
val expDate = authResult.userInfo.expDate// Get all movies
val movies = xtream.movie.getMovies()
// Get movie detail
val movieDetail = xtream.movie.getMovieDetail(streamId = 123456)
// Get movie categories
val categories = xtream.movie.getMovieCategories()
// Get movies by category
val moviesInCategory = xtream.movie.getMoviesByCategory(categoryId = 10)// Get all TV series
val tvSeries = xtream.tvSeries.getTvSeries()
// Get TV series detail
val seriesDetail = xtream.tvSeries.getTvSeriesDetail(seriesId = 789012)
// Get TV series detail (alternative format)
val seriesDetail2 = xtream.tvSeries.getTvSeriesDetail2(seriesId = 789012)
// Get TV series categories
val categories = xtream.tvSeries.getTvSeriesCategories()
// Get TV series by category
val seriesInCategory = xtream.tvSeries.getTvSeriesByCategory(categoryId = 5)// Perform custom GET request
val response: MyCustomModel = xtream.custom.get {
url("https://api.example.com/custom-endpoint")
}
// Get raw HTTP response
val httpResponse = xtream.custom.getHttpResponse {
url("https://api.example.com/endpoint")
}
val statusCode = httpResponse.status.value
val body = httpResponse.bodyAsText()import io.github.saifullah.xtream.url.StreamUrlBuilder
import io.github.saifullah.xtream.model.XtreamAuthCredentials
// Build movie stream URL
val movieUrl = StreamUrlBuilder.buildMovieUrl(
protocol = "https",
host = "server.com",
username = "user",
password = "pass",
streamId = 123456,
extension = "mp4"
)
// Build episode stream URL
val episodeUrl = StreamUrlBuilder.buildEpisodeUrl(
protocol = "https",
host = "server.com",
username = "user",
password = "pass",
episodeId = 789012,
extension = "mkv"
)
// Using credentials object
val credentials = XtreamAuthCredentials(
protocol = "https",
host = "server.com",
port = 8080,
username = "user",
password = "pass"
)
val movieUrl = StreamUrlBuilder.buildMovieUrl(
authCredentials = credentials,
streamId = 123456,
extension = "mp4"
)val xtream = Xtream {
// Authentication
auth {
protocol = "https"
host = "your-server.com"
port = 8080
username = "your_username"
password = "your_password"
}
// Network timeouts (in milliseconds)
socketTimeoutMillis = 60000 // Default: 60000
connectTimeoutMillis = 60000 // Default: 60000
requestTimeoutMillis = 60000 // Default: 60000
useTimeout = true // Default: true
// HTTP settings
followRedirects = true // Default: true
expectSuccess = false // Default: false
// Caching
useCache = true // Default: true
// Retry configuration
maxRetries = 3 // Default: 0 (no retries)
retryDelayMillis = 1000 // Default: 1000
// Custom HTTP client (optional)
// httpClient(OkHttp) {
// // Custom configuration
// }
}import io.github.saifullah.xtream.model.XtreamAuthCredentials
// Use different credentials for a specific request
val customCredentials = XtreamAuthCredentials(
protocol = "https",
host = "other-server.com",
port = 8080,
username = "other_user",
password = "other_pass"
)
val movies = xtream.movie.getMovies(credentials = customCredentials)All API methods throw exceptions that should be handled:
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.ServerResponseException
import kotlinx.serialization.SerializationException
try {
val movies = xtream.movie.getMovies()
} catch (e: ClientRequestException) {
// HTTP 4xx errors
println("Client error: ${e.response.status}")
} catch (e: ServerResponseException) {
// HTTP 5xx errors
println("Server error: ${e.response.status}")
} catch (e: SerializationException) {
// JSON parsing errors
println("Serialization error: ${e.message}")
} catch (e: Exception) {
// Other errors
println("Error: ${e.message}")
}Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)This project is licensed under the MIT License - see the LICENSE file for details.
Saifullah Nurani
If you encounter any issues or have questions, please file an issue on the GitHub repository.
Made with β€οΈ using Kotlin Multiplatform