Skip to content

Commit

Permalink
finish history pagination & other features
Browse files Browse the repository at this point in the history
  • Loading branch information
zaednasr committed Nov 14, 2024
1 parent f6c9b01 commit 448c4bc
Show file tree
Hide file tree
Showing 16 changed files with 192 additions and 163 deletions.
39 changes: 26 additions & 13 deletions app/src/main/java/com/deniscerri/ytdl/database/dao/HistoryDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.room.Query
import androidx.room.Update
import com.deniscerri.ytdl.database.models.DownloadItem
import com.deniscerri.ytdl.database.models.HistoryItem
import com.deniscerri.ytdl.database.repository.HistoryRepository
import kotlinx.coroutines.flow.Flow

@Dao
Expand All @@ -17,46 +18,49 @@ interface HistoryDao {
"CASE WHEN :sort = 'ASC' THEN id END ASC," +
"CASE WHEN :sort = 'DESC' THEN id END DESC," +
"CASE WHEN :sort = '' THEN id END DESC ")
fun getHistorySortedByID(query : String, type : String, site : String, sort : String) : List<HistoryItem>
fun getHistorySortedByIDPaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>

@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
"CASE WHEN :sort = 'ASC' THEN title END ASC," +
"CASE WHEN :sort = 'DESC' THEN title END DESC," +
"CASE WHEN :sort = '' THEN title END DESC ")
fun getHistorySortedByTitle(query : String, type : String, site : String, sort : String) : List<HistoryItem>
fun getHistorySortedByTitlePaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>

@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
"CASE WHEN :sort = 'ASC' THEN author END ASC," +
"CASE WHEN :sort = 'DESC' THEN author END DESC," +
"CASE WHEN :sort = '' THEN author END DESC ")
fun getHistorySortedByAuthor(query : String, type : String, site : String, sort : String) : List<HistoryItem>


fun getHistorySortedByAuthorPaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>

@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
"CASE WHEN :sort = 'ASC' THEN filesize END ASC," +
"CASE WHEN :sort = 'DESC' THEN filesize END DESC," +
"CASE WHEN :sort = '' THEN filesize END DESC ")
fun getHistorySortedByFilesizePaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>

@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
"CASE WHEN :sort = 'ASC' THEN id END ASC," +
"CASE WHEN :sort = 'DESC' THEN id END DESC," +
"CASE WHEN :sort = '' THEN id END DESC ")
fun getHistorySortedByIDPaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>
fun getHistoryIDsSortedByID(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>

@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
"CASE WHEN :sort = 'ASC' THEN title END ASC," +
"CASE WHEN :sort = 'DESC' THEN title END DESC," +
"CASE WHEN :sort = '' THEN title END DESC ")
fun getHistorySortedByTitlePaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>
fun getHistoryIDsSortedByTitle(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>

@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
"CASE WHEN :sort = 'ASC' THEN author END ASC," +
"CASE WHEN :sort = 'DESC' THEN author END DESC," +
"CASE WHEN :sort = '' THEN author END DESC ")
fun getHistorySortedByAuthorPaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>
fun getHistoryIDsSortedByAuthor(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>

@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
"CASE WHEN :sort = 'ASC' THEN filesize END ASC," +
"CASE WHEN :sort = 'DESC' THEN filesize END DESC," +
"CASE WHEN :sort = '' THEN filesize END DESC ")
fun getHistorySortedByFilesizePaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>

fun getHistoryIDsSortedByFilesize(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>

@Query("SELECT * FROM history")
fun getAllHistory() : Flow<List<HistoryItem>>
Expand All @@ -79,6 +83,12 @@ interface HistoryDao {
@Query("SELECT * FROM history WHERE url=:url")
fun getAllHistoryByURL(url: String) : List<HistoryItem>

@Query("SELECT * FROM history WHERE id in (:ids)")
fun getAllHistoryByIDs(ids: List<Long>) : List<HistoryItem>

@Query("SELECT downloadPath FROM history WHERE id in (:ids)")
fun getDownloadPathsFromIDs(ids: List<Long>) : List<HistoryRepository.HistoryItemDownloadPaths>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: HistoryItem)

Expand All @@ -88,6 +98,9 @@ interface HistoryDao {
@Query("DELETE FROM history")
suspend fun deleteAll()

@Query("DELETE FROM history where id in (:ids)")
suspend fun deleteAllByIDs(ids: List<Long>)

@Query("DELETE FROM history WHERE id > (SELECT MIN(h.id) FROM history h WHERE h.url = history.url AND h.type = history.type)")
suspend fun deleteDuplicates()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,17 @@ class HistoryRepository(private val historyDao: HistoryDao) {
return historyDao.getAllHistoryByURL(url)
}

data class HistoryIDsAndPaths(
val id: Long,
val downloadPath: List<String>
)

fun getFiltered(query : String, type : String, site : String, sortType: HistorySortType, sort: SORTING, statusFilter: HistoryViewModel.HistoryStatus) : List<HistoryItem> {
fun getFilteredIDs (query : String, type : String, site : String, sortType: HistorySortType, sort: SORTING, statusFilter: HistoryViewModel.HistoryStatus) : List<Long> {
var filtered = when(sortType){
HistorySortType.DATE -> historyDao.getHistorySortedByID(query, type, site, sort.toString())
HistorySortType.TITLE -> historyDao.getHistorySortedByTitle(query, type, site, sort.toString())
HistorySortType.AUTHOR -> historyDao.getHistorySortedByAuthor(query, type, site, sort.toString())
HistorySortType.FILESIZE -> {
val items = historyDao.getHistorySortedByID(query, type, site, sort.toString())
when(sort){
SORTING.DESC -> items.sortedByDescending { it.format.filesize }
SORTING.ASC -> items.sortedBy { it.format.filesize }
}
}
HistorySortType.DATE -> historyDao.getHistoryIDsSortedByID(query, type, site, sort.toString())
HistorySortType.TITLE -> historyDao.getHistoryIDsSortedByTitle(query, type, site, sort.toString())
HistorySortType.AUTHOR -> historyDao.getHistoryIDsSortedByAuthor(query, type, site, sort.toString())
HistorySortType.FILESIZE -> historyDao.getHistoryIDsSortedByFilesize(query, type, site, sort.toString())
}

when(statusFilter) {
Expand All @@ -59,8 +57,7 @@ class HistoryRepository(private val historyDao: HistoryDao) {
}
else -> {}
}

return filtered
return filtered.map { it.id }
}

fun getPaginatedSource(query : String, type : String, site : String, sortType: HistorySortType, sort: SORTING) : PagingSource<Int, HistoryItem> {
Expand Down Expand Up @@ -101,6 +98,26 @@ class HistoryRepository(private val historyDao: HistoryDao) {
historyDao.deleteAll()
}

suspend fun deleteAllWithIDs(ids: List<Long>, deleteFile: Boolean = false){
if (deleteFile){
historyDao.getAllHistoryByIDs(ids).forEach { item ->
item.downloadPath.forEach {
FileUtil.deleteFile(it)
}
}
}
historyDao.deleteAllByIDs(ids)
}

data class HistoryItemDownloadPaths(
val downloadPath: List<String>
)

fun getDownloadPathsFromIDs(ids: List<Long>) : List<List<String>> {
val res = historyDao.getDownloadPathsFromIDs(ids)
return res.map { it.downloadPath }
}

suspend fun deleteDuplicates(){
historyDao.deleteDuplicates()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
if (resetResults) deleteAll()

//throw YoutubeDLException("Youtube Watch Videos is not yet supported in data fetching. You can download it directly by clicking Continue Anyway or by Quick Downloading it!")
//TODO use below code after youtubedl-android has fixed issue #295 in their repo
val items = mutableListOf<ResultItem>()
val ytExtractorResult = newPipeUtil?.getPlaylistData(inputQuery) {
if (addToResults){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@OptIn(ExperimentalCoroutinesApi::class)
class HistoryViewModel(application: Application) : AndroidViewModel(application) {
Expand All @@ -52,7 +53,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)

var paginatedItems : Flow<PagingData<HistoryItem>>
var websites : Flow<List<String>>
var totalCount : Flow<Int>
var totalCount = MutableStateFlow(0)

data class HistoryFilters(
var type: String = "",
Expand All @@ -67,7 +68,6 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
val dao = DBManager.getInstance(application).historyDao
repository = HistoryRepository(dao)
websites = repository.websites
totalCount = repository.count

val filters = listOf(dao.getAllHistory(), sortOrder, sortType, websiteFilter, statusFilter, queryFilter, typeFilter)
paginatedItems = combine(filters) { f ->
Expand Down Expand Up @@ -107,6 +107,10 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
else -> {}
}

withContext(Dispatchers.IO) {
totalCount.value = repository.getFilteredIDs(query, type, website, sortType, sortOrder, status).count()
}

pager
}.flatMapLatest { it }
}
Expand Down Expand Up @@ -138,9 +142,16 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
statusFilter.value = status
}

private fun filter(query : String, format : String, site : String, sortType: HistorySortType, sort: SORTING, statusFilter: HistoryStatus) = viewModelScope.launch(Dispatchers.IO){

fun getIDsBetweenTwoItems(firstID: Long, secondID: Long): List<Long> {
val ids = repository.getFilteredIDs(queryFilter.value, typeFilter.value, websiteFilter.value, sortType.value, sortOrder.value, statusFilter.value)
val firstIndex = ids.indexOf(firstID)
val secondIndex = ids.indexOf(secondID)
return ids.filterIndexed {index, _ -> index in (firstIndex + 1) until secondIndex }
}

fun getItemIDsNotPresentIn(not: List<Long>) : List<Long> {
val ids = repository.getFilteredIDs(queryFilter.value, typeFilter.value, websiteFilter.value, sortType.value, sortOrder.value, statusFilter.value)
return ids.filter { !not.contains(it) }
}

fun getAll() : List<HistoryItem> {
Expand All @@ -159,6 +170,14 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
repository.delete(item, deleteFile)
}

fun deleteAllWithIDs(ids: List<Long>, deleteFile: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
repository.deleteAllWithIDs(ids, deleteFile)
}

fun getDownloadPathsFromIDs(ids: List<Long>) : List<List<String>> {
return repository.getDownloadPathsFromIDs(ids)
}

fun deleteAll(deleteFile: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
repository.deleteAll(deleteFile)
}
Expand All @@ -175,16 +194,4 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
repository.clearDeletedHistory()
}

fun getRecordsBetweenTwoItems(item1: Long, item2: Long) : List<HistoryItem> {
val filtered = repository.getFiltered(queryFilter.value!!, typeFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, statusFilter.value!!)
val firstIndex = filtered.indexOfFirst { it.id == item1 }
val secondIndex = filtered.indexOfFirst { it.id == item2 }

return if(firstIndex > secondIndex) {
filtered.filterIndexed { index, _ -> index in (secondIndex + 1) until firstIndex }
}else{
filtered.filterIndexed { index, _ -> index in (firstIndex + 1) until secondIndex }
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position) ?: return
val card = holder.cardView
holder.itemView.tag = item.id.toString()
card.tag = item.id.toString()
card.popup()

Expand Down Expand Up @@ -147,7 +148,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
if (btn.hasOnClickListeners()) btn.setOnClickListener(null)
btn.isClickable = filesPresent

if (checkedItems.contains(item.id)) {
if ((checkedItems.contains(item.id) && !inverted) || (!checkedItems.contains(item.id) && inverted)) {
card.isChecked = true
card.strokeWidth = 5
} else {
Expand All @@ -156,12 +157,12 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
}
val finalFilePresent = filesPresent
card.setOnLongClickListener {
checkCard(card, item.id)
checkCard(card, item.id, position)
true
}
card.setOnClickListener {
if (checkedItems.size > 0) {
checkCard(card, item.id)
checkCard(card, item.id, position)
} else {
onItemClickListener.onCardClick(item.id, finalFilePresent)
}
Expand Down Expand Up @@ -208,7 +209,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
}
}

private fun checkCard(card: MaterialCardView, itemID: Long) {
private fun checkCard(card: MaterialCardView, itemID: Long, position: Int) {
if (card.isChecked) {
card.strokeWidth = 0
if (inverted) checkedItems.add(itemID)
Expand All @@ -219,13 +220,13 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
else checkedItems.add(itemID)
}
card.isChecked = !card.isChecked
onItemClickListener.onCardSelect(itemID, card.isChecked)
onItemClickListener.onCardSelect(card.isChecked, position)
}

interface OnItemClickListener {
fun onCardClick(itemID: Long, isPresent: Boolean)
fun onButtonClick(itemID: Long, isPresent: Boolean)
fun onCardSelect(itemID: Long, isChecked: Boolean)
fun onButtonClick(itemID: Long, filePresent: Boolean)
fun onCardClick(itemID: Long, filePresent: Boolean)
fun onCardSelect(isChecked: Boolean, position: Int)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {


scheduleBtn.setOnClickListener{
UiUtil.showDatePicker(fragmentManager) {
UiUtil.showDatePicker(fragmentManager, sharedPreferences) {
lifecycleScope.launch {
resultViewModel.cancelUpdateItemData()
resultViewModel.cancelUpdateFormatsItemData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure


scheduleBtn.setOnClickListener{
UiUtil.showDatePicker(parentFragmentManager) { cal ->
UiUtil.showDatePicker(parentFragmentManager, preferences) { cal ->
toggleLoading(true)
lifecycleScope.launch {
withContext(Dispatchers.IO){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
isFocusable = false
isClickable = false
setOnClickListener{
UiUtil.showTimePicker(fragmentManager){
UiUtil.showTimePicker(fragmentManager, sharedPreferences){
everyTime.editText?.setText(
SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "HHmm"), Locale.getDefault()).format(it.timeInMillis)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
}

override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
TODO("Not yet implemented")

}


Expand Down
Loading

0 comments on commit 448c4bc

Please sign in to comment.