Skip to content

Fix recursive folder sync#16602

Open
l3pr-org wants to merge 8 commits intonextcloud:masterfrom
l3pr-org:fix/recursive-sync
Open

Fix recursive folder sync#16602
l3pr-org wants to merge 8 commits intonextcloud:masterfrom
l3pr-org:fix/recursive-sync

Conversation

@l3pr-org
Copy link

Summary

This PR fixes #269 , the inability to recursively sync folders (an issue that is 10 years old). Previously, when users tried to sync a folder with subfolders recursively, only the immediate children were synced. I spent three days of time I should have been focusing on my programming assignments on this, so please let me know if there is anything wrong with my code instead of just throwing it out. I am happy to collaborate, but I needed this feature so that I can properly sync and listen to my music.

Root Cause

The issue had three contributing factors:

  1. Race Condition: MetadataWorker and FolderDownloadWorker were running without proper dependency, causing FolderDownloadWorker to query the database before metadata was populated.

  2. eTag Caching: MetadataWorker was skipping folder content refresh when eTag hadn't changed, leaving the database empty.

  3. Single-Level Processing: MetadataWorker only fetched one level of subfolders, not recursively traversing into nested folders like Artists/ABBA, for example.

Changes

  • BackgroundJobManagerImpl.kt: Added WorkManager job dependency chain to ensure MetadataWorker runs before FolderDownloadWorker
  • MetadataWorker.kt:
    • Added FORCE_REFRESH flag to bypass eTag caching
    • Implemented BFS-based recursive folder processing to fetch metadata for ALL nested subfolders
  • FolderDownloadWorker.kt: Added debug logging and warning when no files found
  • UI Components: Added "Download all subfolders" option in file actions with confirmation dialog

🏁 Checklist

  • Tests written, or not not needed (existing tests cover core functionality; new functionality requires instrumented tests with a real Nextcloud server)
  • Tested locally on device

Stanley and others added 6 commits February 27, 2026 04:26
- Make FileDataStorageManager.getFolderContent() public to allow MetadataWorker access
- Add recursive parameter to BackgroundJobManager.downloadFolder() with default false
- Add job dependency in BackgroundJobManagerImpl to ensure MetadataWorker runs before FolderDownloadWorker
- Add recursive parameter to FileDownloadHelper.downloadFolder()
- Pass recursive parameter in SynchronizeFolderOperation
- Add FORCE_REFRESH flag to bypass eTag caching and always fetch fresh content
- Implement BFS-based recursive folder processing to fetch metadata for ALL nested subfolders
- Before: Only processed one level of subfolders (e.g., Artists but not ABBA inside it)
- After: Recursively processes all nested folders at every depth level
- Also fetches files at each folder level to ensure FolderDownloadWorker has access to all files
- Add warning log when no files found for recursive download
- Add detailed debug logging in getAllFilesRecursive() to track folder processing
- Log content count at each folder level to help diagnose issues
- Add DOWNLOAD_FOLDER_RECURSIVE action in FileAction
- Add action ID in ids.xml
- Add UI strings for recursive download confirmation dialogs
- Add showDownloadFolderRecursiveConfirmation() in FileActivity
- Add downloadFolderRecursive() method in FileOperationsHelper
- Add overloaded syncFile() method with recursive parameter
- FileDetailFragment: Add handler for action_sync_file_recursive to show recursive download dialog
- OCFileListFragment: Add handler for recursive download action in file list
- PreviewTextFileFragment: Add confirmation dialog for folder sync
@l3pr-org
Copy link
Author

l3pr-org commented Mar 1, 2026

Going to paste a message from the linked issue here:

I may have also forgotten to read the contrib guidelines in this case... whoops 😅 I forgot to sign my commits and went past the line length limit for a couple lines of code, but I can fix these when I have a little more time.

I think even if you (Nextcloud maintainers) take issue with the things I did wrong, we will end up with a working implementation in the end, which is nice.

storageManager.getFolderContent(folder, false)
.filter { !it.isFolder && !it.isDown }
private fun getFiles(folder: OCFile, storageManager: FileDataStorageManager, recursive: Boolean): List<OCFile> {
if (recursive) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please use FiledatastorageManager.getAllFilesRecursivelyInsideFolder() together with the OCFile.isDown check instead of introducing a new function?

Using isDown should not negatively impact this process; in fact, it helps reduce unnecessary operations.

Also, let’s remove the comments where possible. Ideally, the code should be self-explanatory and clear enough that extensive comments aren’t needed.

val files = getFiles(folder, storageManager, recursiveDownload)

// Add warning log when no files found for recursive download
if (files.isEmpty()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need warning log here? It is just extra info if file list is empty.

Please change with info log and remove the emoji along with comment since Log already prints.

/**
* Recursively get all files in the folder and its subfolders
*/
private fun getAllFilesRecursive(folder: OCFile, storageManager: FileDataStorageManager): List<OCFile> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this and use FiledatastorageManager.getAllFilesRecursivelyInsideFolder()

}

override fun downloadFolder(folder: OCFile, accountName: String) {
override fun downloadFolder(folder: OCFile, accountName: String, recursive: Boolean) {
Copy link
Collaborator

@alperozturk96 alperozturk96 Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR and your time.

I’d suggest keeping the scope of this PR strictly limited. The changes proposed outside of FolderDownloadWorker are quite significant and would require thorough testing.

For now, let’s focus only on FolderDownloadWorker with a minimal adjustment, for example, adding the new boolean parameter. From the UI side, we can introduce a separate action for this behavior, and have the worker that uses either the full nested structure or only the first-level depth based on that parameter.

Keeping the scope small will help us review more effectively and move forward with confidence. Once this is merged and validated, we can discuss and plan the next steps separately.

Thanks for understanding and we can test, review other findings in separate PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sync folders + subfolders

3 participants