....
This commit is contained in:
parent
80c06c5ab3
commit
274eaa3213
@ -541,7 +541,11 @@ class ForeGroundService : Service() {
|
|||||||
url?.let {
|
url?.let {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
val youtubeDLDir = File(getExternalFilesDir(null), "completed_torrents")
|
val baseDir = File(getExternalFilesDir(null), "completed_torrents")
|
||||||
|
|
||||||
|
// 2. youtube 전용 폴더 생성
|
||||||
|
val youtubeDLDir = File(baseDir, "Youtube")
|
||||||
|
if (!youtubeDLDir.exists()) youtubeDLDir.mkdirs()
|
||||||
val command = YoutubeDLRequest(url).apply {
|
val command = YoutubeDLRequest(url).apply {
|
||||||
addOption("-q") // 로그 최소화
|
addOption("-q") // 로그 최소화
|
||||||
addOption("--no-warnings") // 경고 숨김
|
addOption("--no-warnings") // 경고 숨김
|
||||||
|
|||||||
@ -232,7 +232,11 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
loadFiles()
|
loadFiles()
|
||||||
}
|
}
|
||||||
.setNeutralButton("폴더 전체 삭제") { _, _ ->
|
.setNeutralButton("폴더 전체 삭제") { _, _ ->
|
||||||
// 💡 여기서도 다이얼로그 안에서 다이얼로그를 부릅니다! (안전한 패턴)
|
if (isProtectedFile(folder)) {
|
||||||
|
Toast.makeText(context, "Images/Videos 폴더는 전체 삭제할 수 없습니다.", Toast.LENGTH_SHORT).show()
|
||||||
|
return@setNeutralButton
|
||||||
|
}
|
||||||
|
|
||||||
android.app.AlertDialog.Builder(requireContext())
|
android.app.AlertDialog.Builder(requireContext())
|
||||||
.setMessage("정말로 폴더와 내부의 모든 파일을 삭제하시겠습니까?")
|
.setMessage("정말로 폴더와 내부의 모든 파일을 삭제하시겠습니까?")
|
||||||
.setPositiveButton("예") { _, _ ->
|
.setPositiveButton("예") { _, _ ->
|
||||||
@ -468,13 +472,32 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
view.findViewById<View>(R.id.btnRenameSelected)?.setOnClickListener { showBatchRenameDialog() }
|
view.findViewById<View>(R.id.btnRenameSelected)?.setOnClickListener { showBatchRenameDialog() }
|
||||||
view.findViewById<View>(R.id.btnDeleteSelected)?.setOnClickListener {
|
view.findViewById<View>(R.id.btnDeleteSelected)?.setOnClickListener {
|
||||||
if (selectedFiles.isEmpty()) return@setOnClickListener
|
if (selectedFiles.isEmpty()) return@setOnClickListener
|
||||||
|
|
||||||
|
// 💡 보호 대상 필터링
|
||||||
|
val protectedCount = selectedFiles.count { isProtectedFile(it) }
|
||||||
|
val filesToDelete = selectedFiles.filter { !isProtectedFile(it) }
|
||||||
|
|
||||||
|
if (protectedCount > 0 && filesToDelete.isEmpty()) {
|
||||||
|
Toast.makeText(context, "Images 및 Videos 폴더 내 항목은 개별 삭제만 가능합니다.", Toast.LENGTH_LONG).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
val message = if (protectedCount > 0) {
|
||||||
|
"보호된 항목 ${protectedCount}개를 제외하고 나머지 ${filesToDelete.size}개 항목을 삭제하시겠습니까?"
|
||||||
|
} else {
|
||||||
|
"선택한 ${filesToDelete.size}개 항목을 삭제하시겠습니까?"
|
||||||
|
}
|
||||||
|
|
||||||
android.app.AlertDialog.Builder(requireContext())
|
android.app.AlertDialog.Builder(requireContext())
|
||||||
.setMessage("선택한 ${selectedFiles.size}개 항목을 삭제하시겠습니까?")
|
.setMessage(message)
|
||||||
.setPositiveButton("삭제") { _, _ ->
|
.setPositiveButton("삭제") { _, _ ->
|
||||||
var delCount = 0
|
var delCount = 0
|
||||||
selectedFiles.forEach { file ->
|
filesToDelete.forEach { file ->
|
||||||
if (file.isDirectory) { file.deleteRecursively(); delCount++ }
|
if (file.isDirectory) {
|
||||||
else if (file.delete()) { delCount++ }
|
if (file.deleteRecursively()) delCount++
|
||||||
|
} else if (file.delete()) {
|
||||||
|
delCount++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Toast.makeText(context, "${delCount}개 항목 삭제됨", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "${delCount}개 항목 삭제됨", Toast.LENGTH_SHORT).show()
|
||||||
toggleSelectionMode(false)
|
toggleSelectionMode(false)
|
||||||
@ -759,9 +782,12 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
|
|
||||||
// walkBottomUp()을 사용하면 가장 깊은 하위 폴더부터 위로 올라오면서 검사합니다.
|
// walkBottomUp()을 사용하면 가장 깊은 하위 폴더부터 위로 올라오면서 검사합니다.
|
||||||
rootDir.walkBottomUp().forEach { dir ->
|
rootDir.walkBottomUp().forEach { dir ->
|
||||||
// 최상위 루트 폴더 자체는 지우면 안 되므로 통과
|
|
||||||
if (dir != rootDir && dir.isDirectory) {
|
if (dir != rootDir && dir.isDirectory) {
|
||||||
// 폴더 내부에 아무것도 없다면 삭제
|
// 💡 Images와 Videos 폴더는 비어있어도 삭제 대상에서 제외
|
||||||
|
if (dir.parentFile == rootDir && (dir.name == "Images" || dir.name == "Videos")) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
if (dir.listFiles()?.isEmpty() == true) {
|
if (dir.listFiles()?.isEmpty() == true) {
|
||||||
if (dir.delete()) {
|
if (dir.delete()) {
|
||||||
deletedFolderCount++
|
deletedFolderCount++
|
||||||
@ -807,6 +833,22 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
if (!hidden) loadFiles()
|
if (!hidden) loadFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isProtectedFile(file: File): Boolean {
|
||||||
|
// 1. 루트의 Images, Videos 폴더 자체 보호
|
||||||
|
val protectedFolders = setOf("Images", "Videos","Youtube")
|
||||||
|
if (file.isDirectory && file.parentFile == rootDir && protectedFolders.contains(file.name)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Images나 Videos 폴더 안에 들어있는 파일/폴더들 보호
|
||||||
|
val parent = file.parentFile
|
||||||
|
if (parent != null && parent.parentFile == rootDir && protectedFolders.contains(parent.name)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun applyFilterAndSort() {
|
private fun applyFilterAndSort() {
|
||||||
// 💡 1. 데이터 소스 결정
|
// 💡 1. 데이터 소스 결정
|
||||||
val baseFiles = if (searchQuery.isNotEmpty()) {
|
val baseFiles = if (searchQuery.isNotEmpty()) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user