This commit is contained in:
lunaticbum 2026-04-08 16:11:52 +09:00
parent 80c06c5ab3
commit 274eaa3213
2 changed files with 54 additions and 8 deletions

View File

@ -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") // 경고 숨김

View File

@ -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()) {