@@ -2,26 +2,43 @@ package com.record.upload
2
2
3
3
import android.content.ContentUris
4
4
import android.content.Context
5
+ import android.media.MediaMetadataRetriever
5
6
import android.net.Uri
6
7
import android.provider.MediaStore
7
8
import android.util.Log
8
9
import androidx.compose.foundation.Image
9
10
import androidx.compose.foundation.layout.Box
10
11
import androidx.compose.foundation.layout.PaddingValues
11
12
import androidx.compose.foundation.layout.fillMaxSize
13
+ import androidx.compose.foundation.layout.height
12
14
import androidx.compose.foundation.layout.padding
13
15
import androidx.compose.foundation.layout.size
16
+ import androidx.compose.foundation.layout.width
17
+ import androidx.compose.material3.Text
14
18
import androidx.compose.runtime.Composable
19
+ import androidx.compose.ui.Alignment
15
20
import androidx.compose.ui.Modifier
16
21
import androidx.compose.ui.draw.clip
17
22
import androidx.compose.ui.graphics.RectangleShape
18
23
import androidx.compose.ui.layout.ContentScale
19
24
import androidx.compose.ui.platform.LocalContext
25
+ import androidx.compose.ui.text.style.TextAlign
20
26
import androidx.compose.ui.unit.dp
27
+ import androidx.compose.ui.unit.sp
21
28
import coil.ImageLoader
22
29
import coil.compose.rememberAsyncImagePainter
23
30
import coil.decode.VideoFrameDecoder
31
+ import com.abedelazizshe.lightcompressorlibrary.CompressionListener
32
+ import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
33
+ import com.abedelazizshe.lightcompressorlibrary.VideoQuality
34
+ import com.abedelazizshe.lightcompressorlibrary.config.Configuration
35
+ import com.abedelazizshe.lightcompressorlibrary.config.SaveLocation
36
+ import com.abedelazizshe.lightcompressorlibrary.config.SharedStorageConfiguration
37
+ import com.record.designsystem.theme.White
24
38
import com.record.ui.extension.customClickable
39
+ import kotlinx.coroutines.CoroutineScope
40
+ import kotlinx.coroutines.Dispatchers
41
+ import kotlinx.coroutines.launch
25
42
26
43
@Composable
27
44
fun SelectedVideoRoute (
@@ -32,7 +49,6 @@ fun SelectedVideoRoute(
32
49
33
50
@Composable
34
51
fun SelectedVideoScreen () {
35
- Log .d(" images" , " ${getAllVideos(10 , null , LocalContext .current)} " )
36
52
val a = getAllVideos(10 , null , LocalContext .current)
37
53
}
38
54
@@ -53,11 +69,10 @@ fun VideoThumbnail(
53
69
model = video.filepath,
54
70
imageLoader = imageLoader,
55
71
)
56
-
57
72
Box (
58
73
modifier = Modifier
59
- .padding( 8 .dp)
60
- .size( 85 .dp),
74
+ .width( 100 .dp)
75
+ .height( 100 .dp),
61
76
) {
62
77
Image (
63
78
painter = painter,
@@ -66,9 +81,16 @@ fun VideoThumbnail(
66
81
modifier = Modifier
67
82
.fillMaxSize()
68
83
.clip(RectangleShape )
69
- .customClickable(
70
- onClick = { setVideo(video) },
71
- ),
84
+ .customClickable { setVideo(video) },
85
+ )
86
+ Text (
87
+ text = formatDuration(video.duration),
88
+ fontSize = 12 .sp,
89
+ color = White ,
90
+ modifier = Modifier
91
+ .align(Alignment .BottomEnd )
92
+ .padding(bottom = 4 .dp, end = 6 .dp),
93
+ textAlign = TextAlign .End ,
72
94
)
73
95
}
74
96
}
@@ -116,9 +138,8 @@ fun getAllVideos(
116
138
val name = cursor.getString(nameColumn)
117
139
val size = cursor.getInt(sizeColumn)
118
140
val date = cursor.getString(dateColumn)
119
-
120
141
val contentUri = ContentUris .withAppendedId(uriExternal, id)
121
- Log .d( " gallery contentUri " , " $ contentUri" )
142
+ val duration = getVideoDuration(context, contentUri)
122
143
galleryVideoList.add(
123
144
GalleryVideo (
124
145
id,
@@ -127,6 +148,7 @@ fun getAllVideos(
127
148
name = name,
128
149
date = date ? : " " ,
129
150
size = size,
151
+ duration = duration,
130
152
),
131
153
)
132
154
} while (cursor.moveToNext())
@@ -143,4 +165,74 @@ data class GalleryVideo(
143
165
val name : String ,
144
166
val date : String ,
145
167
val size : Int ,
168
+ val duration : Long ,
146
169
)
170
+
171
+ fun getVideoDuration (context : Context , uri : Uri ): Long {
172
+ val retriever = MediaMetadataRetriever ()
173
+ return try {
174
+ retriever.setDataSource(context, uri)
175
+ val time = retriever.extractMetadata(MediaMetadataRetriever .METADATA_KEY_DURATION )
176
+ val timeInMillisec = time?.toLongOrNull() ? : 0L
177
+ timeInMillisec
178
+ } catch (e: Exception ) {
179
+ e.printStackTrace()
180
+ 0L
181
+ } finally {
182
+ retriever.release()
183
+ }
184
+ }
185
+
186
+ fun compressVideo (context : Context , videoUri : Uri , name : String ) {
187
+ CoroutineScope (Dispatchers .IO ).launch {
188
+ VideoCompressor .start(
189
+ context = context,
190
+ uris = listOf (videoUri),
191
+ isStreamable = false ,
192
+ sharedStorageConfiguration = SharedStorageConfiguration (
193
+ saveAt = SaveLocation .movies,
194
+ subFolderName = " compressed_videos" ,
195
+ ),
196
+ configureWith = Configuration (
197
+ quality = VideoQuality .MEDIUM ,
198
+ isMinBitrateCheckEnabled = true ,
199
+ videoBitrateInMbps = 5 ,
200
+ disableAudio = false ,
201
+ keepOriginalResolution = false ,
202
+ videoNames = listOf (name),
203
+ ),
204
+ listener = object : CompressionListener {
205
+ override fun onProgress (index : Int , percent : Float ) {
206
+ // 압축 진행 상황 업데이트
207
+ Log .d(" video onProgress" , " $index , $percent " )
208
+ }
209
+
210
+ override fun onStart (index : Int ) {
211
+ // 압축 시작 시 호출
212
+ Log .d(" video onStart" , " $index " )
213
+ }
214
+
215
+ override fun onSuccess (index : Int , size : Long , path : String? ) {
216
+ // 압축 성공 시 호출
217
+ Log .d(" video onSuccess" , " $index $size $path " )
218
+ }
219
+
220
+ override fun onFailure (index : Int , failureMessage : String ) {
221
+ // 압축 실패 시 호출
222
+ Log .d(" video onFailure" , " onFailure" )
223
+ }
224
+
225
+ override fun onCancelled (index : Int ) {
226
+ // 압축 취소 시 호출
227
+ Log .d(" video onCancelled" , " onCancelled" )
228
+ }
229
+ },
230
+ )
231
+ }
232
+ }
233
+
234
+ fun formatDuration (durationMillis : Long ): String {
235
+ val minutes = (durationMillis / 1000 ) / 60
236
+ val seconds = (durationMillis / 1000 ) % 60
237
+ return String .format(" %d:%02d" , minutes, seconds)
238
+ }
0 commit comments