Skip to content

Commit

Permalink
Merge branch 'main' into feature/25-custom-headers
Browse files Browse the repository at this point in the history
  • Loading branch information
GrakovNe authored Nov 7, 2024
2 parents 77cb9a0 + 904bcb8 commit 4469e2c
Show file tree
Hide file tree
Showing 22 changed files with 216 additions and 137 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/build_app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Build Lissen App

env:
# The name of the main module repository
main_project_module: app

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

workflow_dispatch:

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

# Set Current Date As Env Variable
- name: Set current date as env variable
run: echo "date_today=$(date +'%Y-%m-%d')" >> $GITHUB_ENV

# Set Repository Name As Env Variable
- name: Set repository name as env variable
run: echo "repository_name=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV

- name: Set Up JDK
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '21'

- name: Change wrapper permissions
run: chmod +x ./gradlew

# Run Build Project
- name: Build gradle project
run: ./gradlew build -Proom.schemaLocation=$GITHUB_WORKSPACE/app/schemas
36 changes: 28 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
# Lissen - Clean Audiobookshelf Player
[![Build Lissen App](https://github.com/GrakovNe/lissen-android/actions/workflows/build_app.yml/badge.svg)](https://github.com/GrakovNe/lissen-android/actions/workflows/build_app.yml)

<p align="center">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/1.png" alt="Screenshot 1" width="200">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/2.png" alt="Screenshot 2" width="200">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/3.png" alt="Screenshot 3" width="200">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/4.png" alt="Screenshot 4" width="200">
<p align="center">
<a href="https://play.google.com/store/apps/details?id=org.grakovne.lissen"><img src="https://upload.wikimedia.org/wikipedia/commons/7/78/Google_Play_Store_badge_EN.svg" alt="Get it on Google Play" height="60"></a>&nbsp;&nbsp;&nbsp;<!--
--><a href="https://f-droid.org/packages/org.grakovne.lissen"><img src="https://upload.wikimedia.org/wikipedia/commons/a/a3/Get_it_on_F-Droid_%28material_design%29.svg" alt="Get it on F-Droid" height="60"></a>
</p>



### Features

* Beautiful Interface: Intuitive design that makes browsing and listening to your audiobooks easy and enjoyable.
* Cloud Sync: Automatically syncs your audiobook progress across devices, keeping everything up to date no matter where you are.
* Streaming Support: Stream your audiobooks directly from the cloud without needing to download them first.
* Offline Listening: Download audiobooks to listen offline, ideal for those who want to access their collection without an internet connection.

### Installation
### Screenshots

<p align="center">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/1.png" alt="Screenshot 1" width="200">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/2.png" alt="Screenshot 2" width="200">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/3.png" alt="Screenshot 3" width="200">
<img src="https://github.com/GrakovNe/lissen-android/raw/main/metadata/en-US/images/phoneScreenshots/4.png" alt="Screenshot 4" width="200">
</p>

### Building

1. Clone the repository:
```
Expand All @@ -35,5 +41,19 @@ nano local.properties
```
5. Build and run the app on an Android device or emulator.

### Demo Environment


You can connect to a demo [Audiobookshelf](https://github.com/advplyr/audiobookshelf) instance through the Lissen app:

```
URL: https://demo.lissenapp.org
Username: demo
Password: demo
```

This instance contains only Public Domain audiobooks from [LibriVox](https://librivox.org/) and is intended solely for demonstrating the client’s functionality.

## License
Lissen is open-source and licensed under the MIT License. See the LICENSE file for more details.
3 changes: 0 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ dependencies {
implementation(libs.androidx.media3.session)
kapt(libs.hilt.android.compiler)

implementation(libs.icons.lucide)

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand All @@ -121,7 +119,6 @@ dependencies {

implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
annotationProcessor(libs.androidx.room.compiler)
ksp(libs.androidx.room.compiler)

testImplementation(libs.junit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.net.Uri
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import org.grakovne.lissen.BuildConfig
import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfDataRepository
import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfMediaRepository
import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfSyncService
Expand All @@ -14,7 +15,9 @@ import org.grakovne.lissen.channel.audiobookshelf.converter.LibraryResponseConve
import org.grakovne.lissen.channel.audiobookshelf.converter.LibrarySearchItemsConverter
import org.grakovne.lissen.channel.audiobookshelf.converter.PlaybackSessionResponseConverter
import org.grakovne.lissen.channel.audiobookshelf.converter.RecentBookResponseConverter
import org.grakovne.lissen.channel.audiobookshelf.model.DeviceInfo
import org.grakovne.lissen.channel.audiobookshelf.model.LibraryResponse
import org.grakovne.lissen.channel.audiobookshelf.model.StartPlaybackRequest
import org.grakovne.lissen.channel.common.ApiResult
import org.grakovne.lissen.channel.common.ApiResult.Success
import org.grakovne.lissen.channel.common.ChannelCode
Expand Down Expand Up @@ -146,9 +149,9 @@ class AudiobookshelfChannel @Inject constructor(
supportedMimeTypes: List<String>,
deviceId: String
): ApiResult<PlaybackSession> {
val request = org.grakovne.lissen.channel.audiobookshelf.model.StartPlaybackRequest(
val request = StartPlaybackRequest(
supportedMimeTypes = supportedMimeTypes,
deviceInfo = org.grakovne.lissen.channel.audiobookshelf.model.DeviceInfo(
deviceInfo = DeviceInfo(
clientName = getClientName(),
deviceId = deviceId,
deviceName = getClientName()
Expand Down Expand Up @@ -196,7 +199,7 @@ class AudiobookshelfChannel @Inject constructor(
password: String
): ApiResult<UserAccount> = dataRepository.authorize(host, username, password)

private fun getClientName() = "Lissen App Android"
private fun getClientName() = "Lissen App ${BuildConfig.VERSION_NAME}"

private val supportedLibraryTypes = listOf("book")
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,23 @@ class LibraryItemIdResponseConverter @Inject constructor() {
}

val filesAsChapters: () -> List<BookChapter> = {
item.media.audioFiles.fold(0.0 to mutableListOf<BookChapter>()) { (accDuration, chapters), file ->
chapters.add(
BookChapter(
start = accDuration,
end = accDuration + file.duration,
title = file.metaTags?.tagTitle
?: file.metadata.filename.removeSuffix(file.metadata.ext),
duration = file.duration,
id = file.ino
item
.media
.audioFiles
.sortedBy { it.index }
.fold(0.0 to mutableListOf<BookChapter>()) { (accDuration, chapters), file ->
chapters.add(
BookChapter(
start = accDuration,
end = accDuration + file.duration,
title = file.metaTags?.tagTitle
?: file.metadata.filename.removeSuffix(file.metadata.ext),
duration = file.duration,
id = file.ino
)
)
)
accDuration + file.duration to chapters
}.second
accDuration + file.duration to chapters
}.second
}

return DetailedBook(
Expand All @@ -54,6 +58,7 @@ class LibraryItemIdResponseConverter @Inject constructor() {
files = item
.media
.audioFiles
.sortedBy { it.index }
.map {
BookFile(
id = it.ino,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ class LibraryItemResponseConverter @Inject constructor() {

fun apply(response: LibraryItemsResponse): PagedItems<Book> = response
.results
.map {
.mapNotNull {
val title = it.media.metadata.title ?: return@mapNotNull null

Book(
id = it.id,
title = it.media.metadata.title,
title = title,
author = it.media.metadata.authorName,
cachedState = BookCachedState.ABLE_TO_CACHE,
duration = it.media.duration.toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import javax.inject.Singleton
class LibrarySearchItemsConverter @Inject constructor() {
fun apply(response: List<LibraryItem>): List<Book> {
return response
.map {
.mapNotNull {
val title = it.media.metadata.title ?: return@mapNotNull null

Book(
id = it.id,
title = it.media.metadata.title,
title = title,
author = it.media.metadata.authorName,
cachedState = BookCachedState.ABLE_TO_CACHE,
duration = it.media.duration.toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,20 @@ package org.grakovne.lissen.channel.audiobookshelf.model

data class LibraryItemsResponse(
val results: List<LibraryItem>,
val total: Int,
val limit: Int,
val page: Int,
val sortBy: String,
val sortDesc: Boolean,
val filterBy: String,
val mediaType: String,
val minified: Boolean,
val collapseseries: Boolean,
val include: String
val page: Int
)

data class LibraryItem(
val id: String,
val libraryId: String,
val folderId: String,
val path: String,
val relPath: String,
val isFile: Boolean,
val mtimeMs: Long,
val ctimeMs: Long,
val birthtimeMs: Long,
val addedAt: Long,
val updatedAt: Long,
val isMissing: Boolean,
val isInvalid: Boolean,
val mediaType: String,
val media: Media
)

data class Media(
val numTracks: Int,
val numAudioFiles: Int,
val numChapters: Int,
val duration: Double,
val metadata: Metadata,
val size: Long
val metadata: Metadata
)

data class Metadata(
val title: String,
val titleIgnorePrefix: String,
val subtitle: String?,
val authorName: String?,
val genres: List<String>,
val publishedYear: String?,
val publishedDate: String?,
val publisher: String?,
val description: String?,
val isbn: String?,
val asin: String?,
val language: String?,
val explicit: Boolean
val title: String?,
val authorName: String?
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package org.grakovne.lissen.channel.common
import android.os.Build
import org.grakovne.lissen.BuildConfig

val USER_AGENT = "Lissen/${BuildConfig.VERSION_NAME} (Linux; Android ${Build.VERSION.RELEASE}; ${Build.MODEL}) ExoPlayer/1.4.1"
val USER_AGENT = "Lissen/${BuildConfig.VERSION_NAME} (Linux; Android ${Build.VERSION.RELEASE}; ${Build.MODEL}) ExoPlayer/1.4.1"
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.RewriteQueriesToDropUnusedColumns
import androidx.room.Transaction
import androidx.room.Update
import org.grakovne.lissen.content.cache.entity.BookChapterEntity
Expand Down Expand Up @@ -82,6 +83,7 @@ interface CachedBookDao {
): List<BookEntity>

@Transaction
@RewriteQueriesToDropUnusedColumns
@Query(
"""
SELECT * FROM detailed_books
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import androidx.core.content.ContextCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.localbroadcastmanager.content.LocalBroadcastManager
Expand Down Expand Up @@ -130,7 +129,7 @@ class MediaRepository @Inject constructor(
val intent = Intent(context, PlaybackService::class.java).apply {
action = PlaybackService.ACTION_PLAY
}
ContextCompat.startForegroundService(context, intent)
context.startForegroundService(intent)
}

fun pauseAudio() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import androidx.compose.ui.layout.ContentScale
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.valentinilk.shimmer.shimmer

@Composable
fun AsyncShimmeringImage(
Expand All @@ -37,7 +36,6 @@ fun AsyncShimmeringImage(
Box(
modifier = Modifier
.fillMaxSize()
.shimmer()
.background(Color.Gray)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import android.content.Context
import android.net.Uri
import coil.ImageLoader
import coil.decode.ImageSource
import coil.disk.DiskCache
import coil.fetch.FetchResult
import coil.fetch.Fetcher
import coil.fetch.SourceResult
import coil.memory.MemoryCache
import coil.request.Options
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -72,17 +70,7 @@ object ImageLoaderModule {
): ImageLoader {
return ImageLoader
.Builder(context)
.components {
add(bookCoverFetcherFactory)
}
.memoryCache {
MemoryCache.Builder(context).build()
}
.diskCache {
DiskCache.Builder()
.directory(context.cacheDir.resolve("сover_cache"))
.build()
}
.components { add(bookCoverFetcherFactory) }
.build()
}
}
Loading

0 comments on commit 4469e2c

Please sign in to comment.