One limitation of the script is that it relies on each Kontakt library having an "Instruments" folder in order to know what the library's main directory is, so in my case this required re-organizing some of my third-party libraries and batch-resaving them with Kontakt.
The script uses the "ln" command line tool. It can be installed via chocolatey with "choco install ln" or via winget with "winget install HermannSchinagl.ln".
How it works:
- The script loops through the folders defined in "libraryDirs" recursively, skipping over folder names contained in "excludeFolders", looking for Kontakt library folders
- For each Kontakt library folder, it looks for a file containing an "nicnt" extension and skips over that library if present (Kontakt manages those snapshots automatically)
- If there is no nicnt file, the script checks for a Snapshots folder, and attempts to match up the snapshots with any .nki instruments within the library
- The script then creates symlinks for each instrument snapshots folder at the location %A_MyDocuments%\Native Instruments\User Content\Kontakt\%InstrumentName%\Factory
This directory structure with the "Factory" symlink allows you to store your user-level snapshots outside of the "Factory" directory so that they are stored within My Documents and not within each library folder.
This was thrown together fairly quickly and could use improvements, but I've found it extremely useful already and thought I would share. I'll add version details and a changelog if/when I update this post.
The "libraryDirs" and "excludeFolders" variables should be updated for your setup, and the rest should probably work as-is.
Code: Select all
libraryDirs := [
"C:\Music\Libraries",
"D:\Music\Libraries",
"E:\Music\Libraries",
]
excludeFolders := [
"IK Multimedia",
"MNTRA",
"Native Instruments User Content",
"Reaktor User Library",
]
includeExts := [
"nkc",
"nki",
"nkm",
"nkr",
]
excludeExts := [
"nicnt",
]
snapshotFolders := [
"Snapshots",
"_Snapshots"
]
factoryFolders := [
"Factory",
"_Factory"
]
snapshotPattern := "*.nksn"
snapshotsDir := A_MyDocuments "\Native Instruments\User Content\Kontakt"
installed := 0
for , libraryDir in libraryDirs {
installed += InstallChildLibrarySnapshots(libraryDir)
}
MsgBox("Installed " installed " snapshot folders.")
InstallChildLibrarySnapshots(parentDir) {
global excludeFolders, excludeExts
innerInstalled := 0
loop files, parentDir "\*", "D" {
for , excludeFolder in excludeFolders {
if (A_LoopFileName == excludeFolder) {
exclude := true
continue 2
}
}
if (IsLibrary(A_LoopFileFullPath)) {
exclude := false
loop files, A_LoopFileFullPath "\*", "F" {
for , excludeExt in excludeExts {
if (A_LoopFileExt == excludeExt) {
exclude := true
break 2
}
}
}
if (!exclude) {
innerInstalled += InstallSnapshotsFromLibrary(A_LoopFileFullPath, A_LoopFileName)
}
} else {
innerInstalled += InstallChildLibrarySnapshots(A_LoopFileFullPath)
}
}
return innerInstalled
}
IsLibrary(libraryFolder) {
isLibrary := false
if (!isLibrary && DirExist(libraryFolder "\Instruments")) {
isLibrary := DirHasLibraryFiles(libraryFolder "\Instruments")
}
return isLibrary
}
DirHasLibraryFiles(dir) {
global includeExts
hasLibraryFiles := false
loop files, dir "\*", "F" {
for , includeExt in includeExts {
if (A_LoopFileExt == includeExt) {
hasLibraryFiles := true
break 2
}
}
}
return hasLibraryFiles
}
IsFactoryFolder(dir) {
global factoryFolders
SplitPath(dir, &dirName)
isFactoryFolder := false
for , factoryFolder in factoryFolders {
if (dirName == factoryFolder) {
isFactoryFolder := true
break
}
}
return isFactoryFolder
}
InstallSnapshotsFromLibrary(libraryFolder, libraryName) {
global snapshotsDir
librarySnapshots := CollectSnapshotsLinks(libraryFolder, libraryName)
innerInstalled := 0
for instrumentName, factoryDir in librarySnapshots {
instrumentDir := snapshotsDir "\" instrumentName
instrumentLinks := Map()
if (FileExist(instrumentDir) && !DirExist(instrumentDir)) {
MsgBox(libraryName ": File '" instrumentDir "' already exists, but is not a directory. Skipping.")
continue
}
factoryFolderName := "Factory"
if (IsSnapshotFolder(factoryDir)) {
SplitPath(factoryDir, &dirName)
if (IsFactoryFolder(factoryDir)) {
factoryFolderName := dirName
}
instrumentLinks[factoryFolderName] := factoryDir
}
for linkName, linkPath in instrumentLinks {
link := instrumentDir "\" linkName
if (!FileExist(linkPath)) {
MsgBox(libraryName ": Dir '" linkPath "' does not exist. Skipping.")
continue
}
if (!FileExist(link)) {
msg := "Install " linkName "?"
msg .= "`n`nLibrary: " libraryFolder
msg .= "`nSource: " SubStr(linkPath, StrLen(libraryFolder) + 2)
msg .= "`nDestination: " instrumentName "\" linkName
result := MsgBox(msg, libraryName, "YesNo")
if (result == "Yes") {
if (!FileExist(instrumentDir)) {
DirCreate(instrumentDir)
}
RunWait("ln --symbolic `"" linkPath "`" `"" link "`"",, "Hide")
innerInstalled += 1
}
}
}
}
return innerInstalled
}
CollectInstrumentNames(libraryFolder) {
global instrumentExts
instrumentNames := []
loop files, libraryFolder "\Instruments\*.nki", "FR" {
instrumentName := SubStr(A_LoopFileName, 1, -(StrLen(A_LoopFileExt) + 1))
exists := false
for , existingName in instrumentNames {
if (existingName == instrumentName) {
exists := true
break
}
}
if (!exists) {
instrumentNames.Push(instrumentName)
}
}
return instrumentNames
}
IsInstrumentName(libraryDir, name) {
instrumentNames := CollectInstrumentNames(libraryDir)
exists := false
for , instrumentName in instrumentNames {
if (instrumentName == name) {
exists := true
break
}
}
return exists
}
GetMainInstrument(libraryDir, libraryName) {
if (IsInstrumentName(libraryDir, libraryName)) {
return libraryName
}
instrumentNames := CollectInstrumentNames(libraryDir)
if (instrumentNames.Length == 1) {
return instrumentNames[1]
}
return ""
}
IsSnapshotFolder(folder) {
global snapshotPattern
isSnapshotFolder := false
loop files, folder "\" snapshotPattern, "FR" {
isSnapshotFolder := true
break
}
return isSnapshotFolder
}
CollectSnapshotsLinks(libraryFolder, libraryName) {
global snapshotFolders, snapshotPattern, snapshotsMap, snapshotsDir
snapshotsPath := ""
librarySnapshots := Map()
librarySnapshots.CaseSense := false
mainInstrument := GetMainInstrument(libraryFolder, libraryName)
for , snapshotFolderName in snapshotFolders {
loop files, libraryFolder "\" snapshotFolderName, "DR" {
if (IsSnapshotFolder(A_LoopFileFullPath)) {
snapshotFolder := A_LoopFileFullPath
nonInstrumentDirs := []
hasInstrumentDirs := false
loop files, snapshotFolder "\*", "D" {
if (IsInstrumentName(libraryFolder, A_LoopFileName) && IsSnapshotFolder(A_LoopFileFullPath)) {
hasInstrumentDirs := true
librarySnapshots[A_LoopFileName] := A_LoopFileFullPath
} else {
nonInstrumentDirs.Push(A_LoopFileFullPath)
}
}
if (nonInstrumentDirs.Length) {
if (hasInstrumentDirs) {
MsgBox("Found mixed content in snapshot dir '" snapshotFolder "'.")
} else if (mainInstrument && !librarySnapshots.Has(mainInstrument)) {
librarySnapshots[mainInstrument] := snapshotFolder
}
}
}
}
}
return librarySnapshots
}