Smart Folder Path

Hi all -

I’m sure everyone has experienced this problem: folders get moved around, then code falls apart.

I was hoping to program a contingency for missing files that would allow a person to re-assign valid paths to path names that return nil.

I’ve run into a few roadblocks figuring it out, so I thought I’d ask here if anyone has implemented something similar as a quark or shared something along these lines in the past, rather than trying to reinvent the wheel.

Otherwise - here’s the current roadblock… the issue is that it reads all of the storedPaths at once, creating a flurry of dialog boxes when there is a problem. I think the logical thing to do is to increment through the storedPaths and have the ability to check against a new specified folder, if a file is missing… I’m not sure how to do either of these things.

	

//recalls from an indexed file a series of stored paths.
storedPaths.do{|x|
	if (x != nil, {    
					if (PathName(x[0].asString).isFile, {
						//if path is found, do nothing.
					},
		{   //if path isn't found, open a dialog window, where new folder can be assigned.
						{
							var win, newPath;
							win = Window("", Rect(100, 900, 350, 100)).front;
							StaticText(win, Rect(10, 40, 250, 80)).string_(x[0].asString);
							StaticText(win, Rect(10, 10, 50, 40)).string_("new path?");
							Button(win, (Rect(280, 10, 40, 40)))
							.string_("folder")
							.action_({|a|
								FileDialog.new({|paths| paths[0].postln;}, 
									{ "cancel".postln;})});
							TextField(win, Rect(60, 10, 210, 40)).action_({|x|
								x.value});
						}.defer;
					});
				});
			};
			

Thanks for the help!

Maybe something to think about: I normally put all required external files in subfolders of the folder where I store my “project” (i.e. the .scd files) and then construct the path to such resources relative to the thisProcess.nowExecutingPath. Like that, if I move the project folder to a different location, everything still works as before.

2 Likes

Yes, I’d always put files in a folder next to the code and then use resolveRelative to make all paths relative to where the code is saved.

But for large files, or files that are used from different locations, the problem ryanscott describes persists.

Normally, I’d say that this should be solved by the file system and symlinks or aliases.
But unfortunately SuperCollider doesn’t seem to resolve.

The primitive for the undocumented method realPath is broken here.

2 Likes

Yes, in my particular case, I am working with a variety of audio samples that may be part of other projects and would prefer to not duplicate the files.

My strategy, when I open a project, is to use method File.exists on all files first to verify they exist.
For any missing files, I can either replace them individually (in my case using a gui with Dialog.openPanel), or else search for them in a chosen folder using Pathname method myPathname.filename to extract the filenames.
Edit: Note - to choose a folder you need to use FileDialog.new (not Dialog.openPanel).
Hope that helps,
Paul

Thanks, @TXMod - I think I was starting to use the FileDialog.new in the example code above. The hurdle I’m struggling with - and maybe you’ve dealt with also - is that it seems to open all the FileDialog boxes at once, when there is a mis-allocated set of references - and I’m wondering if there is a slightly more gentle, intelligent way of doing this, where it goes through each file, one at a time, and then checks subsequent files against the newly specified path.

How about something like this:

  1. Look through all the paths and store any missing ones into an array.
  2. If the missing array isn’t empty, then open a dialog window, where a folder can be chosen.
  3. Search for all missing files in chosen folder and store any still-missing files in a new missing array.
  4. Go back to step 2 again so you can try another folder for the still-missing files.
  5. … until you’ve found all the files or cancelled.

Best,
Paul

The other way is to use VScode to fix the path string. In VScode you can open a folder, then you can find all the path strings in SCD files in that folder and subfolders, and you can replace the path string with the new path.

Generally, if you want to send your project to other people or duplicate your project on other machines, the best way is to copy all the sound files into a subfolder of the folder where the supercollider document is located. Then thisProcess.nowExecutingPath would be the easiest way to use the path. One problem with this method is that there will be too many duplicate files in your memory. The documentation for this method is a bit confusing. The following is my PR to supplement the documentation:

However, if you are using it alone with a shared folder with other projects for sound files outside the folder where the SCD file is located, then using symbolic links might solve the problem. However, I am not sure if SuperCollider can recognise symbolic links correctly. SuperCollider may not handle symbolic links in the same way depending on the operating system.

You might look at the Require quark, which handles path lookup for both relative locations, and against one or more “global” paths. It’s meant for loading .scd files, but the path resolution can be used to find e.g. audio or data files using the same logic.

Require("/path/to/a/file.scd"); // runs an scd at an absolute path

Require.roots.add("/path/to/my/files");          // add a global path
Require.roots.add("/path/to/my/other_files");    // add a global path

Require("instrument");   // attempts to load:
                         //   ./instrument(.scd)      (relative)
                         //   /path/to/my/files/instrument(.scd)
                         //   /path/to/my/other_files/instrument(.scd)

Have you considered making a pr to include this in the class library? Working with files, other sc files and audio files, is such a core task of basically every supercollider project.

I see your point, and I’d love to see it more commonly used, but I don’t think it makes sense in the core library. It’s not tightly coupled to anything in the core classlib, nor does anything else in the classlib needs it, and the general arc is to split the classlib apart versus add more things. I want the flexibility to updating it more often than new SuperCollider releases go out.

Quarks like this might make sense in a set “core tools” that could be recommended for every beginning user - but I’ll leave that to someone else, of course I think my own quarks are impossible to do without (after all, I made them for myself first :slight_smile: ).