Flatten folder? / Syncthing-Fork run custom script after folder sync completed

I have an iPhone and it syncs to both a server and an Android phone. The Android is set to receive-only, everything works flawlessly. I would like to set up some kind of automation so that the date folders from iOS get flattened only on Android, after being synced, not sure how to do that.

Do the files appear in the synced folder only after they are fully downloaded or does it also appear in a partial way (e.g. like torrents)?

If anyone has ideas on how to set up automatic flattening on a non-rooted Android phone, I’m all ears. If not, perhaps there is a thing that would be possible with Tasker or some other tool which can run a linux command like:

find "$TARGET" -mindepth 2 -type f -exec mv -n "{}" "$TARGET/" \;
find "$TARGET" -mindepth 1 -type d -empty -delete

Maybe you could use a termux script?

1 Like

Thank you, thinking of that, just did not want to go through the hassle since I’ve never used it, but it would be able to watch folders I guess. Another option is Macrodroid, which I suppose can run scripts.

But are the files in the synced folder available fully once they are present or do I need to wait for a while? I want to avoid moving partial files, not syre how Syncthing works behind the hood.

There are two potential downsides to flattening the directory tree:

  • The Android phone will be shown as perpetually out-of-sync due to the local changes.
  • Depending on the Android version, syncing to a single directory with a large number of files will slowly drag down sync speeds.

Syncthing’s “Understanding Synchronization” page has all the details about temporary files during transfer and renaming in place.

1 Like

Files are renamed when complete.

The Android phone will be shown as perpetually out-of-sync due to the local changes.
Depending on the Android version, syncing to a single directory with a large number of files will slowly drag down sync speeds.

That is by design, the Android device will regularly be emptied. I kept a large folder in the previous implementations but it was pretty performant as-is.

Files are renamed when complete.

Are they renamed in the same folder? The documentation hints that this is the case, with *.tmp files. The hidden attribute would be a quick selection if that is the case.

Sorry if I misused the forum formatting and quoting, getting used to it. Also autocorrect does not work for some reason.

Files that are being sent by Syncthing get a .tmp extension, in the same folder, during that process. Once the file is fully synced and the blocks all match, it is renamed to the original filename.

1 Like

I’ve tried setting up a script using termux and inotify but the problem is that the termux script does not have the permissions to read/write the files. I assume this happens because SAF was used for the folder to be synced but termux has its own symlinks. Without root, no idea how to proceed with this. Is there any post-sync hook that I can use to run a script?

I am not sure if this would work, maybe the wrapper’s EventProcessor which handles file changes could be extended to run a custom script with the changed file as an argument. This first would have to be implemented by a PR.

But there is no way to chmod o+w on the files and folders, right? This would at least allow me to run the scripts. I will research some more, worst case I can perhaps use a termux folder for syncing.

I don’t know but suspect may be difficult on vfat/selinux implementations Android uses under the hood. Scripts /could/ possibly be run from the internal lib folder like we do it with our libsyncthingnative.so

1 Like

Hi @testing123,

I’ve implemented custom script support into the Syncthing-Fork app. The wrapper can now run shell scripts when a folder completes its sync progress. I’m using it myself to clean up mess Android itself repeatedly puts in my folders to prevent it from syncing across to other connected devices.

You can optIn to test it by turning expert options on and upgrading to this test build:

https://github.com/Catfriend1/syncthing-android/actions/runs/15002361443/artifacts/3116423884

For security considerations, you have to put your scripts into “.stfolder” and turn that option explicitly on per folder. See Expert option: Run custom shell script after folder sync completed by Catfriend1 · Pull Request #1412 · Catfriend1/syncthing-android · GitHub for more details and demo scripts.

Kind Regards, Catfriend1

3 Likes

Wow, that was fast! Will try it and report back, thank you.

1 Like

It seems to work, now I need to figure out why my script is not working :slight_smile:

05-14 15:04:25.820 11872 11872 D RestApi : setRemoteCompletionInfo: Completed folder=[iOS]
05-14 15:04:25.822 11872 11872 D Util    : runShellCommandGetOutput: cd "/storage/emulated/0/iOS/.stfolder/..";sh "/storage/emulated/0/iOS/.stfolder/flatten-folder-after-sync.sh" "sync_complete"
05-14 15:04:25.849 11872 11872 V Util    : runScriptSet: Exec result []
05-14 15:04:25.852 11872 11872 D Util    : runShellCommandGetOutput: cd "/storage/emulated/0/iOS/.stfolder/..";sh "/storage/emulated/0/iOS/.stfolder/demo1.sh" "sync_complete"
05-14 15:04:25.866 11872 11872 V Util    : runScriptSet: Exec result [[INFO] Demo 1: param1=[sync_complete], pwd=[/storage/emulated/0/iOS]

Got it working, it was a noob problem, CRLF line endings in the script. Here’s the one I use for flattening the structure (flatten-folder-after-sync.sh):

#!/bin/sh
TARGET="./"

# Move files from all visible subdirectories to $TARGET
find "$TARGET" -mindepth 2 -type f ! -path '*/.*' -exec mv -n "{}" "$TARGET/" \;

# Delete only empty, non-hidden subdirectories
find "$TARGET" -mindepth 1 -type d -empty ! -path '*/.*' -delete

exit 0
1 Like

Yay, 100! :100:

I will flatten my folders too, then :smiley:.

1 Like