Skip to content

Commit 994cbbc

Browse files
Merge pull request #34 from codewithtamim/master
Add CUE
2 parents c843c66 + 0af31dc commit 994cbbc

File tree

15 files changed

+1076
-49
lines changed

15 files changed

+1076
-49
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ android {
1515
applicationId = packageName
1616
minSdk = 21
1717
targetSdk = 36
18-
versionCode = 33
19-
versionName = "1.3.2-lts"
18+
versionCode = 34
19+
versionName = "1.3.3-prerelease"
2020
multiDexEnabled = true
2121

2222
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

app/src/main/java/com/nasahacker/convertit/data/repository/AudioConverterRepositoryImpl.kt

Lines changed: 352 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.nasahacker.convertit.domain.model
2+
3+
/**
4+
* Convertit Android app
5+
* <a href="https://github.com/thebytearray/Convertit">GitHub Repository</a>
6+
*
7+
* Created by Tamim Hossain.
8+
* Copyright (c) 2025 The Byte Array LTD.
9+
*
10+
* This file is part of the Convertit Android app.
11+
*
12+
* The Convertit Android app is free software: you can redistribute it and/or
13+
* modify it under the terms of the Apache License, Version 2.0 as published by
14+
* the Apache Software Foundation.
15+
*
16+
* The Convertit Android app is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18+
* or FITNESS FOR A PARTICULAR PURPOSE. See the Apache License for more
19+
* details.
20+
*
21+
* You should have received a copy of the Apache License
22+
* along with the Convertit Android app. If not, see <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
23+
*
24+
* @author Tamim Hossain
25+
* @company The Byte Array LTD
26+
* @year 2025
27+
* @license Apache-2.0
28+
*/
29+
data class CueTrack(
30+
val trackNumber: Int,
31+
val title: String,
32+
val performer: String? = null,
33+
val startTime: String,
34+
val endTime: String? = null,
35+
val startTimeSeconds: Double,
36+
val endTimeSeconds: Double? = null
37+
)
38+
39+
data class CueFile(
40+
val title: String? = null,
41+
val performer: String? = null,
42+
val file: String? = null,
43+
val tracks: List<CueTrack> = emptyList()
44+
) {
45+
fun hasValidTracks(): Boolean = tracks.size > 1
46+
fun getTracksWithEndTimes(): List<CueTrack> {
47+
if (tracks.isEmpty()) return emptyList()
48+
49+
return tracks.mapIndexed { index, track ->
50+
val endTime = if (index < tracks.size - 1) {
51+
tracks[index + 1].startTime
52+
} else {
53+
null
54+
}
55+
56+
val endTimeSeconds = if (index < tracks.size - 1) {
57+
tracks[index + 1].startTimeSeconds
58+
} else {
59+
null
60+
}
61+
62+
track.copy(
63+
endTime = endTime,
64+
endTimeSeconds = endTimeSeconds
65+
)
66+
}
67+
}
68+
}

app/src/main/java/com/nasahacker/convertit/domain/repository/AudioConverterRepository.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,27 @@ interface AudioConverterRepository {
5353
onFailure: (String) -> Unit,
5454
onProgress: (Int) -> Unit,
5555
)
56+
57+
suspend fun convertWithCueSplitting(
58+
customSaveUri: Uri?,
59+
playbackSpeed: String,
60+
uri: Uri,
61+
outputFormat: AudioFormat,
62+
bitrate: AudioBitrate,
63+
onSuccess: (List<String>) -> Unit,
64+
onFailure: (String) -> Unit,
65+
onProgress: (Int) -> Unit,
66+
)
67+
68+
suspend fun convertWithManualCue(
69+
customSaveUri: Uri?,
70+
playbackSpeed: String,
71+
audioUri: Uri,
72+
cueUri: Uri,
73+
outputFormat: AudioFormat,
74+
bitrate: AudioBitrate,
75+
onSuccess: (List<String>) -> Unit,
76+
onFailure: (String) -> Unit,
77+
onProgress: (Int) -> Unit,
78+
)
5679
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.nasahacker.convertit.domain.usecase
2+
3+
import android.net.Uri
4+
import com.nasahacker.convertit.domain.model.AudioBitrate
5+
import com.nasahacker.convertit.domain.model.AudioFormat
6+
import com.nasahacker.convertit.domain.repository.AudioConverterRepository
7+
import javax.inject.Inject
8+
9+
/**
10+
* Convertit Android app
11+
* <a href="https://github.com/thebytearray/Convertit">GitHub Repository</a>
12+
*
13+
* Created by Tamim Hossain.
14+
* Copyright (c) 2025 The Byte Array LTD.
15+
*
16+
* This file is part of the Convertit Android app.
17+
*
18+
* The Convertit Android app is free software: you can redistribute it and/or
19+
* modify it under the terms of the Apache License, Version 2.0 as published by
20+
* the Apache Software Foundation.
21+
*
22+
* The Convertit Android app is distributed in the hope that it will be useful,
23+
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24+
* or FITNESS FOR A PARTICULAR PURPOSE. See the Apache License for more
25+
* details.
26+
*
27+
* You should have received a copy of the Apache License
28+
* along with the Convertit Android app. If not, see <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
29+
*
30+
* @author Tamim Hossain
31+
* @company The Byte Array LTD
32+
* @year 2025
33+
* @license Apache-2.0
34+
*/
35+
class ConvertAudioWithCueUseCase @Inject constructor(
36+
private val audioConverterRepository: AudioConverterRepository
37+
) {
38+
39+
suspend operator fun invoke(
40+
customSaveUri: Uri?,
41+
playbackSpeed: String,
42+
uri: Uri,
43+
outputFormat: AudioFormat,
44+
bitrate: AudioBitrate,
45+
onSuccess: (List<String>) -> Unit,
46+
onFailure: (String) -> Unit,
47+
onProgress: (Int) -> Unit,
48+
) {
49+
audioConverterRepository.convertWithCueSplitting(
50+
customSaveUri = customSaveUri,
51+
playbackSpeed = playbackSpeed,
52+
uri = uri,
53+
outputFormat = outputFormat,
54+
bitrate = bitrate,
55+
onSuccess = onSuccess,
56+
onFailure = onFailure,
57+
onProgress = onProgress
58+
)
59+
}
60+
}

app/src/main/java/com/nasahacker/convertit/service/ConvertItService.kt

Lines changed: 86 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import com.nasahacker.convertit.util.AppConfig.AUDIO_PLAYBACK_SPEED
5151
import com.nasahacker.convertit.util.AppConfig.BITRATE
5252
import com.nasahacker.convertit.util.AppConfig.CHANNEL_ID
5353
import com.nasahacker.convertit.util.AppConfig.CONVERT_BROADCAST_ACTION
54+
import com.nasahacker.convertit.util.AppConfig.CUE_URI
5455
import com.nasahacker.convertit.util.AppConfig.IS_SUCCESS
5556
import com.nasahacker.convertit.util.AppConfig.URI_LIST
5657
import dagger.hilt.android.AndroidEntryPoint
@@ -133,6 +134,11 @@ class ConvertItService : Service() {
133134
val bitrate = AudioBitrate.fromBitrate(intent?.getStringExtra(BITRATE))
134135
val format = AudioFormat.fromExtension(intent?.getStringExtra(AUDIO_FORMAT))
135136
val speed = intent?.getStringExtra(AUDIO_PLAYBACK_SPEED) ?: "1.0"
137+
val cueUri: Uri? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
138+
intent?.getParcelableExtra(CUE_URI, Uri::class.java)
139+
} else {
140+
intent?.getParcelableExtra(CUE_URI)
141+
}
136142

137143
Log.d(
138144
TAG,
@@ -142,6 +148,7 @@ class ConvertItService : Service() {
142148
- Bitrate: ${bitrate.bitrate}
143149
- Playback Speed: $speed
144150
- Number of files: ${uriList?.size ?: 0}
151+
- CUE file: ${cueUri?.lastPathSegment ?: "None"}
145152
- Files: ${uriList?.joinToString { it.lastPathSegment ?: "unknown" }}
146153
""".trimIndent(),
147154
)
@@ -174,42 +181,85 @@ class ConvertItService : Service() {
174181
null
175182
}
176183

177-
audioConverterRepository.performConversion(
178-
customSaveUri = customSaveUri,
179-
playbackSpeed = speed,
180-
uris = uriList,
181-
outputFormat = format,
182-
bitrate = bitrate,
183-
onSuccess = {
184-
Log.i(
185-
TAG,
186-
"Conversion completed successfully for all files. startId: $startId",
187-
)
188-
showCompletionNotification(true)
189-
broadcastConversionResult(
190-
Intent().apply {
191-
action = CONVERT_BROADCAST_ACTION
192-
},
193-
true,
194-
)
195-
stopForegroundService()
196-
},
197-
onFailure = { error ->
198-
Log.e(TAG, "Conversion failed with error: $error. startId: $startId")
199-
showCompletionNotification(false)
200-
broadcastConversionResult(
201-
Intent().apply {
202-
action = CONVERT_BROADCAST_ACTION
203-
},
204-
false,
205-
)
206-
stopForegroundService()
207-
},
208-
onProgress = { progress ->
209-
Log.v(TAG, "Conversion progress: $progress%. startId: $startId")
210-
updateNotification(progress)
211-
},
212-
)
184+
if (cueUri != null && uriList.size == 1) {
185+
// CUE-based conversion
186+
Log.i(TAG, "Starting CUE-based conversion with CUE file: ${cueUri.lastPathSegment}")
187+
audioConverterRepository.convertWithManualCue(
188+
customSaveUri = customSaveUri,
189+
playbackSpeed = speed,
190+
audioUri = uriList.first(),
191+
cueUri = cueUri,
192+
outputFormat = format,
193+
bitrate = bitrate,
194+
onSuccess = {
195+
Log.i(
196+
TAG,
197+
"CUE-based conversion completed successfully. startId: $startId",
198+
)
199+
showCompletionNotification(true)
200+
broadcastConversionResult(
201+
Intent().apply {
202+
action = CONVERT_BROADCAST_ACTION
203+
},
204+
true,
205+
)
206+
stopForegroundService()
207+
},
208+
onFailure = { error ->
209+
Log.e(TAG, "CUE-based conversion failed with error: $error. startId: $startId")
210+
showCompletionNotification(false)
211+
broadcastConversionResult(
212+
Intent().apply {
213+
action = CONVERT_BROADCAST_ACTION
214+
},
215+
false,
216+
)
217+
stopForegroundService()
218+
},
219+
onProgress = { progress ->
220+
Log.v(TAG, "CUE conversion progress: $progress%. startId: $startId")
221+
updateNotification(progress)
222+
},
223+
)
224+
} else {
225+
// Regular conversion
226+
audioConverterRepository.performConversion(
227+
customSaveUri = customSaveUri,
228+
playbackSpeed = speed,
229+
uris = uriList,
230+
outputFormat = format,
231+
bitrate = bitrate,
232+
onSuccess = {
233+
Log.i(
234+
TAG,
235+
"Conversion completed successfully for all files. startId: $startId",
236+
)
237+
showCompletionNotification(true)
238+
broadcastConversionResult(
239+
Intent().apply {
240+
action = CONVERT_BROADCAST_ACTION
241+
},
242+
true,
243+
)
244+
stopForegroundService()
245+
},
246+
onFailure = { error ->
247+
Log.e(TAG, "Conversion failed with error: $error. startId: $startId")
248+
showCompletionNotification(false)
249+
broadcastConversionResult(
250+
Intent().apply {
251+
action = CONVERT_BROADCAST_ACTION
252+
},
253+
false,
254+
)
255+
stopForegroundService()
256+
},
257+
onProgress = { progress ->
258+
Log.v(TAG, "Conversion progress: $progress%. startId: $startId")
259+
updateNotification(progress)
260+
},
261+
)
262+
}
213263
} catch (e: Exception) {
214264
Log.e(TAG, "Exception during conversion: ${e.message}. startId: $startId")
215265
showCompletionNotification(false)

0 commit comments

Comments
 (0)