Looping trough folders of a parent folder

I’ve created this script where I want to display the name of each file having mp4 as his extension for all subfolders of a parent folder.

i.e I other word I am attempting to: loop through subfolders of a parent folder.

I do not know what my problem is.

Thanks!

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AVFoundation"
use scripting additions

set MP4DestinationFolder to POSIX path of "/Volumes/DATA/ZVinformatique/CompétitionCourante/"
--set MP4DestinationFolder to POSIX path of (choose folder)
tell application "Finder"
	
	set thefolders to every folder of entire contents of MP4DestinationFolder
	repeat with objItem in thefolders
		display dialog "folder" & objItem
		set mp4Files to (files in folder objItem whose name extension is "mp4") as alias list
		
		if (count of mp4Files) is equal to 0 then
			display dialog ("Aucun fichier mp4 dans le dossier " & MP4DestinationFolder)
			return
		end if
		set countofmp4 to (count of mp4Files) as string
		display dialog (countofmp4 & " fichier vidéo de format mp4 seront traiter " & MP4DestinationFolder)
		repeat with fvideo in mp4Files
			display dialog ("will be converting " & fvideo)
			--my createclipvideo(fvideo)
		end repeat
	end repeat
end tell

The main problem is that the Finder doesn’t accept POSIX paths.

The syntax

POSIX path of "/Volumes/DATA/ZVinformatique/CompétitionCourante/"

is pointless anyway because the literal string is a POSIX path.

I recommend to use an alias with an HFS path (starting with a disk name). And remove the return line because a return statement on the top level will exit the script. Add an else clause instead.

set MP4DestinationFolder to alias "DATA:ZVinformatique:CompétitionCourante:"

tell application "Finder"
	
	set thefolders to every folder of entire contents of MP4DestinationFolder
	repeat with objItem in thefolders
		display dialog "folder " & objItem
		set mp4Files to (files in objItem whose name extension is "mp4")
		set countofmp4 to (count of mp4Files)
		if countofmp4 is equal to 0 then
			display dialog ("Aucun fichier mp4 dans le dossier " & MP4DestinationFolder)
		else
			display dialog ((countofmp4 as text) & " fichier vidéo de format mp4 seront traiter " & MP4DestinationFolder)
			repeat with fvideo in mp4Files
				display dialog ("will be converting " & fvideo)
				--my createclipvideo(fvideo)
			end repeat
		end if
	end repeat
end tell

Thanks Stefank,

This small script is to be attached to a longer working script. I am now attaching the whole script.

The script is to create short video clips of longer video files found in a given folder. At the time, I’ve started this project I was to execute the script subfolder by subfolder individually. The situation has changed where I can no longer execute the script subfolders by subfolders.

To speed uptake execution time, I have to verify the clip already exist. If it does, the script is toby pass that file and go to the next one.

Thanks again for your help.

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AVFoundation"
use scripting additions

set MP4DestinationFolder to alias "Volumes:DATA:ZVinformatique:CompétitionCourante:"


--Get the parent folder for which the CLIP function must be executed
tell application "Finder"
	-- find all subfloder name
	set thefolders to every folder of entire contents of MP4DestinationFolder
	
	-- Go through each subolders found in the parent folder
	repeat with objItem in thefolders
		display dialog "folder " & objItem
		set mp4Files to (files in objItem whose name extension is "mp4")
		set countofmp4 to (count of mp4Files)
		if countofmp4 is equal to 0 then
			display dialog ("Aucun fichier mp4 dans le dossier " & MP4DestinationFolder)
		else
			repeat with fvideo in mp4Files
				
				--Verify if clip already exist, do not recreate clip if already exist
				if exists file fvideo then
					return
				else
					
					--display dialog ("will be converting " & fvideo)
					--call the CLIP function
					my createclipvideo(fvideo)
				end if
			end repeat
		end if
	end repeat
end tell

on createclipvideo(fvideo)
	set thePath to POSIX path of (fvideo)
	set theURL to current application's NSURL's fileURLWithPath:thePath
	set theAsset to current application's AVURLAsset's assetWithURL:theURL
	set destPath to (theURL's |path|()'s stringByDeletingPathExtension()'s stringByAppendingString:"-clip")'s stringByAppendingPathExtension:"mp4"
	
	
	set durationInfo to theAsset's duration()
	if durationInfo is missing value then
		set theTime to "unknown"
	else
		-- calculate duration in seconds and format as string
		set theDuration to (value of durationInfo) / (timescale of durationInfo)
	end if
	set theStart to (0.65 * theDuration) div 1
	set theEnd to 15
	if theStart + 15 > theDuration then
		set theStart to (0.55 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to (0.4 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to (0.25 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to 1
		set theEnd to theDuration
	end if
	set destURL to current application's NSURL's fileURLWithPath:destPath
	set startTime to current application's CMTimeMakeWithSeconds(theStart, 1)
	set durTime to current application's CMTimeMakeWithSeconds(15, 1)
	set theRange to current application's CMTimeRangeMake(startTime, durTime)
	set theAsset to current application's AVURLAsset's assetWithURL:theURL
	set exportSession to current application's AVAssetExportSession's alloc()'s initWithAsset:theAsset presetName:(current application's AVAssetExportPresetHighestQuality)
	exportSession's setOutputURL:destURL
	exportSession's setOutputFileType:(current application's AVFileTypeQuickTimeMovie)
	exportSession's setTimeRange:theRange
	exportSession's exportAsynchronouslyWithCompletionHandler:(missing value)
	repeat
		delay 0.01
		set theStatus to exportSession's status()
		if theStatus > 2 then exit repeat -- 3 is OK
	end repeat
	if theStatus = 4 then error exportSession's |error|()'s localizedDescription() as text
end createclipvideo

As I said an HFS path starts always with a disk name, never with Volumes, even for external volumes.

The script is almost complete. I only have problems with verifying if the file exist. I’ve been trying different combination without success. THANKS!


set thePath to alias "Volumes:DATA:ZVinformatique:CompétitionCourante:01:pzv01001.mp4" as string

set the_colon_location to offset of "." in thePath
set the_starting_point to the_colon_location - 2
set the_ending_point to the_colon_location - 1
set theClipPath to characters 1 thru the_ending_point of thePath as string
set theClipPath to "Volumes:" & theClipPath & "-clip.mp4" as alias

display dialog ("***" & theClipPath & "***") as string
if not (exists file theClipPath) then
	display dialog ("will be converting " & theClipPath)
	--call the CLIP function
	my createclipvideo(fvideo)
end if

on createclipvideo(fvideo)
	set thePath to POSIX path of ("Volumes:" & fvideo)
end createclipvideo

You’re not going to have any success if you keep trying to use HFS paths and ignore Stefan’s point about not using Volumes.

Given that you’re already using ASObjC, and you’re having problems with how HFS paths work, you’d probably be better to avoid them and the Finder altogether:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AVFoundation"
use scripting additions


set MP4DestinationFolder to "/Volumes/DATA/ZVinformatique/CompétitionCourante/"
set destURL to current application's |NSURL|'s fileURLWithPath:MP4DestinationFolder
set theOptions to (current application's NSDirectoryEnumerationSkipsHiddenFiles) + (get current application's NSDirectoryEnumerationSkipsPackageDescendants)
set theURLs to (current application's NSFileManager's defaultManager()'s enumeratorAtURL:destURL includingPropertiesForKeys:(missing value) options:theOptions errorHandler:(missing value))'s allObjects()
set thePred to current application's NSPredicate's predicateWithFormat_("pathExtension == %@", "mp4")
set theURLs to theURLs's filteredArrayUsingPredicate:thePred
repeat with aURL in theURLs
	display dialog ("will be converting " & aURL's |path|() as text)
	my createclipvideo(fvideo)
end repeat

on createclipvideo(theURL)
	set theAsset to current application's AVURLAsset's assetWithURL:theURL
	set destPath to (theURL's |path|()'s stringByDeletingPathExtension()'s stringByAppendingString:"-clip")'s stringByAppendingPathExtension:"mp4"
	
	
	set durationInfo to theAsset's duration()
	if durationInfo is missing value then
		set theTime to "unknown"
	else
		-- calculate duration in seconds and format as string
		set theDuration to (value of durationInfo) / (timescale of durationInfo)
	end if
	set theStart to (0.65 * theDuration) div 1
	set theEnd to 15
	if theStart + 15 > theDuration then
		set theStart to (0.55 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to (0.4 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to (0.25 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to 1
		set theEnd to theDuration
	end if
	set destURL to current application's NSURL's fileURLWithPath:destPath
	set startTime to current application's CMTimeMakeWithSeconds(theStart, 1)
	set durTime to current application's CMTimeMakeWithSeconds(15, 1)
	set theRange to current application's CMTimeRangeMake(startTime, durTime)
	set theAsset to current application's AVURLAsset's assetWithURL:theURL
	set exportSession to current application's AVAssetExportSession's alloc()'s initWithAsset:theAsset presetName:(current application's AVAssetExportPresetHighestQuality)
	exportSession's setOutputURL:destURL
	exportSession's setOutputFileType:(current application's AVFileTypeQuickTimeMovie)
	exportSession's setTimeRange:theRange
	exportSession's exportAsynchronouslyWithCompletionHandler:(missing value)
	repeat
		delay 0.01
		set theStatus to exportSession's status()
		if theStatus > 2 then exit repeat -- 3 is OK
	end repeat
	if theStatus = 4 then error exportSession's |error|()'s localizedDescription() as text
end createclipvideo

Thanks Shane and Stefank,

Sorry about this!

The situation here is that I do not know the differences between HFS’ path and alias. Neither I do understand their behaviour. As of using AObjC, sincerely I do have no experiences.

This script has been growing up by browsing through the internet and asking questions on the MacScripter forum. I have been very fortunate to have people like you on MacScripter.

The AObjC module works very good in building short video clips. I have been using it over the weekend and found I’ve needed to improve the process in which the script was to be executed.

The script is to be executed over a folder name “/DATA/ZVinformatique/CompétitionCourante/xx/” being stored on a NAS by the name of “FARAMIR”. Over a given week-end I will be creating over 250 videos which are going to be of 2, 3, 4, up to 6 minutes.

Under “CompétitionCourante”, there will be up to 99 subfolders being created. Each subfolders will be containing the video files and 15 seconds clips of those video files.

I will be running the script as many time I will be having “xx” subfolders being created. I then need to find a mechanism which avoid trying to recreate clips which have already been created. This is what I what I was trying to do last. i.e skip re-creating a video 15 seconds video which has already been created.

Where could I get some information in order to code the last part being needed?

If I understand correct, I should be using POSIX path names instead of HFS path names?

With regards!

Daniel

You use HFS paths if you want to create aliases to pass to applications like the Finder. HFS paths are colon-delimited and never begin with Volumes – they begin with the name of the volume, as it appears in the Finder. POSIX paths are slash-delimited, and for remote volumes will begin with “/Volumes/”.

Where the script says:

on createclipvideo(theURL)
   set theAsset to current application's AVURLAsset's assetWithURL:theURL
   set destPath to (theURL's |path|()'s stringByDeletingPathExtension()'s stringByAppendingString:"-clip")'s stringByAppendingPathExtension:"mp4"

Insert the following two lines:

set destURL to current application's NSURL's fileURLWithPath:destPath
if (destURL's checkResourceIsReachableAndReturnError:(missing value)) as boolean then return

That will check if the file exists, and if so go no further with it.

Thanks again for your help.

Unfortunately at the moment, it keeps running even if the “-clip” file already exist.

Original file : pzv01001.mp4
First time the script run create: pzv01001-clip.mp4
Second time the script run creates : pzv01001-clip-clip.mp4
Third time the script run creates : pzv01001-clip-clip-clip.mp4

on createclipvideo(fvideo)
	set thePath to POSIX path of (fvideo)
	set theURL to current application's NSURL's fileURLWithPath:thePath
	set theAsset to current application's AVURLAsset's assetWithURL:theURL
	set destPath to (theURL's |path|()'s stringByDeletingPathExtension()'s stringByAppendingString:"-clip")'s stringByAppendingPathExtension:"mp4"
	set destURL to current application's NSURL's fileURLWithPath:destPath
	if (destURL's checkResourceIsReachableAndReturnError:(missing value)) as boolean then return

It doesn’t really. It’s just that as you search the folder each time for all .mp4 files, it catches the -clip files and therefore tries to make clips of them. You need to filter them out at the beginning.

So change this line:

set thePred to current application's NSPredicate's predicateWithFormat_("pathExtension == %@", "mp4")

To this:

set thePred to current application's NSPredicate's predicateWithFormat:"(pathExtension == %@) AND (lastPathComponent.stringByDeletingPathExtension ENDSWITH %@)" argumentArray:{"mp4", "-clip"}

I see what you are saying.

At the moment with the new line it does not get into the repeat with aURL in theURLs loop.

If should only process “.mp4” files but not those mp4 files ending by “-clip.mp4”

For example, the first time “pzv01001.mp4” should be processed to create “pzv01001-clip.mp4”.

The next time the script is being processed, a 15 second clip should not be created for “pzv01001.mp4” s because “pzv01001-clip.mp4” already exist.

Let’s say:
pzv01001.mp4 look if (pzv01001-clip.mp4 exist ; yes-skip ; no process to create pzv01001-clip.mp4)
pzv01002.mp4 look if (pzv01002-clip.mp4 exist ; yes-skip ; no process to create pzv01002-clip.mp4)

Thanks!

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AVFoundation"
use scripting additions


set MP4DestinationFolder to "/Volumes/DATA/ZVinformatique/CompétitionCourante/"
set destURL to current application's |NSURL|'s fileURLWithPath:MP4DestinationFolder
set theOptions to (current application's NSDirectoryEnumerationSkipsHiddenFiles) + (get current application's NSDirectoryEnumerationSkipsPackageDescendants)
set theURLs to (current application's NSFileManager's defaultManager()'s enumeratorAtURL:destURL includingPropertiesForKeys:(missing value) options:theOptions errorHandler:(missing value))'s allObjects()
--set thePred to current application's NSPredicate's predicateWithFormat_("pathExtension == %@", "mp4")
set thePred to current application's NSPredicate's predicateWithFormat:"(pathExtension == %@) AND (lastPathComponent.stringByDeletingPathExtension ENDSWITH %@)" argumentArray:{"mp4", "-clip"}
set theURLs to theURLs's filteredArrayUsingPredicate:thePred

repeat with aURL in theURLs
	--display dialog ("will be converting " & aURL's |path|() as text)
	my createclipvideo(aURL)
end repeat
display dialog ("after repeat")

on createclipvideo(theURL)
	set theAsset to current application's AVURLAsset's assetWithURL:theURL
	set destPath to (theURL's |path|()'s stringByDeletingPathExtension()'s stringByAppendingString:"-clip")'s stringByAppendingPathExtension:"mp4"
	set destURL to current application's NSURL's fileURLWithPath:destPath
	if (destURL's checkResourceIsReachableAndReturnError:(missing value)) as boolean then return
	
	set durationInfo to theAsset's duration()
	if durationInfo is missing value then
		set theTime to "unknown"
	else
		-- calculate duration in seconds and format as string
		set theDuration to (value of durationInfo) / (timescale of durationInfo)
	end if
	set theStart to (0.65 * theDuration) div 1
	set theEnd to 15
	if theStart + 15 > theDuration then
		set theStart to (0.55 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to (0.4 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to (0.25 * theDuration) div 1
		set theEnd to 15
	end if
	if theStart + 15 > theDuration then
		set theStart to 1
		set theEnd to theDuration
	end if
	set destURL to current application's NSURL's fileURLWithPath:destPath
	set startTime to current application's CMTimeMakeWithSeconds(theStart, 1)
	set durTime to current application's CMTimeMakeWithSeconds(15, 1)
	set theRange to current application's CMTimeRangeMake(startTime, durTime)
	set theAsset to current application's AVURLAsset's assetWithURL:theURL
	set exportSession to current application's AVAssetExportSession's alloc()'s initWithAsset:theAsset presetName:(current application's AVAssetExportPresetHighestQuality)
	exportSession's setOutputURL:destURL
	exportSession's setOutputFileType:(current application's AVFileTypeQuickTimeMovie)
	exportSession's setTimeRange:theRange
	exportSession's exportAsynchronouslyWithCompletionHandler:(missing value)
	repeat
		delay 0.01
		set theStatus to exportSession's status()
		if theStatus > 2 then exit repeat -- 3 is OK
	end repeat
	if theStatus = 4 then error exportSession's |error|()'s localizedDescription() as text
end createclipvideo

Oops, I left out a NOT:

set thePred to current application's NSPredicate's predicateWithFormat:"(pathExtension == %@) AND (NOT lastPathComponent.stringByDeletingPathExtension ENDSWITH %@)" argumentArray:{"mp4", "-clip"}

WOW! This one was a VERY big one for me to accomplished.

My process of recording videos with short clips that accompany them will make it easier for me. This is something I wanted to do for many years. I knew this was going to be a VERY BIG challenge for me.

I want to say thanks to all of those who helped me out with this. A special thanks to Stefank and to Shane Stanley. Impossible for me to this without your help.

I will try in the future to be more careful with HFS path (:slight_smile: no “Volumes” and POSIX path (/) “with remote volume (external disk) will start with /Volumes/” paths.