-- FixLinks - V1.08 - © Steve MacGuire - 2022-10-22 -- An AppleScript to repair broken track links in Music or iTunes -- Downloaded from https://discussions.apple.com/docs/DOC-250002671 set mediaPath to "" -- preset media folder here with form // here to avoid opening prompt set showMissing to true -- set to false to suppress any reporting of missing tracks set maxLen to 0 -- use to truncate predicted paths to a max. length, e.g. 40 to match iTunes for Windows -- tell application "iTunes" -- Mojave or earlier tell application "Music" -- Catalina or later if selection is {} then display dialog "Please select some tracks with broken links before calling this script" with title "FixLinks" else set mySelection to selection set m to 0 set p to 0 set u to 0 set s to 0 repeat with aTrack in mySelection set p to p + 1 set aPath to "" try set aPath to location of aTrack as text end try -- display dialog aPath & return & mediaPath -- enable for debugging if mediaPath is "" then set mediaPath to my getMediaPath() -- evaluate artist folder set theArtist to album artist of aTrack if theArtist = "" then set theArtist to artist of aTrack if theArtist = "" then set theArtist to "Unknown Artist" if compilation of aTrack = true then set theArtist to "Compilations" set theArtist to my safePath(theArtist) -- evaluate album folder set theAlbum to album of aTrack if theAlbum = "" then set theAlbum to "Unknown Album" set theAlbum to my safePath(theAlbum) -- evaluate file name set theFile to "" if track number of aTrack > 0 then set theFile to theFile & track number of aTrack & " " if track number of aTrack < 10 then set theFile to "0" & theFile -- add leading disc number if implied if disc number of aTrack > 1 or disc count of aTrack > 1 then set theFile to (disc number of aTrack as text) & "-" & theFile -- or always add leading disc number -- if disc number of aTrack > 0 then set theFile to (disc number of aTrack as text) & "-" & theFile end if set theFile to theFile & name of aTrack set theFile to my safeFile(theFile, true) -- calculate file extension set k to kind of aTrack if k = "AAC audio file" then set ext to ".m4a" else if k = "AIFF audio file" then set ext to ".aif" else if k = "Apple Lossless audio file" then set ext to ".m4a" else if k = "Apple Music AAC audio file" then set ext to ".m4p" else if k = "iTunes LP" then set ext to ".itlp" else if k = "Matched AAC audio file" then set ext to ".m4a" else if k = "MPEG audio file" then set ext to ".mp3" else if k = "MPEG-4 video file" then set ext to ".mp4" else if k = "PDF document" then set ext to ".pdf" else if k = "Purchased AAC audio file" then set ext to ".m4a" else if k = "Purchased MPEG-4 video file" then set ext to ".mp4" -- might also be ".m4v", alternatives will be checked later else if k = "WAV audio file" then set ext to ".wav" else display dialog "Edit script to provide extension for tracks of kind " & k with title "FixLinks" set ext to "" end if -- extend this pattern as required set newPath to theArtist & "/" & theAlbum & "/" & theFile -- Normal //[D-]## . layout -- set newPath to theArtist & "/" & theFile -- Option for no album folders within artist folders set testPath to mediaPath & newPath & ext if not my fileExists(testPath) then set testPath to mediaPath & newPath & " 1" & ext if not my fileExists(testPath) then set testPath to mediaPath & newPath & " 2" & ext if not my fileExists(testPath) then set testPath to mediaPath & "Music/" & newPath & ext if not my fileExists(testPath) then set testPath to mediaPath & "Music/" & newPath & " 1" & ext if not my fileExists(testPath) then set testPath to mediaPath & "Music/" & newPath & " 2" & ext if ext = ".mp4" then if not my fileExists(testPath) then set testPath to mediaPath & newPath & ".m4v" if not my fileExists(testPath) then set testPath to mediaPath & newPath & " 1.m4v" if not my fileExists(testPath) then set testPath to mediaPath & "Music/" & newPath & ".m4v" if not my fileExists(testPath) then set testPath to mediaPath & "Music/" & newPath & " 1.m4v" end if -- pattern above could be extended for other variations, e.g. trailing 2 -- display dialog mediaPath & return & testPath -- enable for debugging if my fileExists(testPath) then -- display dialog "Connecting broken track to:" & return & testPath & with title "FixLinks" -- enable for debugging try -- Workaround for Big Sur -- error "Music got an error: Parameter error." number -50  set location of aTrack to testPath end try -- note this script needs tweaking to correctly repair broken iTunes LP entries set u to u + 1 else set m to m + 1 if showMissing then set msg to "File not found at:" & return & return & mediaPath & newPath & ext set msg to msg & return & return & "Continue reporting?" set answer to the button returned of (display dialog msg buttons {"Yes", "No", "Cancel"} with title "FixLinks") if answer = "No" then set showMissing to false end if end if end repeat -- report on script activity set msg to (p as string) & " item" set msg to msg & my plural(p, "s were", " was") set msg to msg & " processed," set msg to msg & return & (u as string) & " item" set msg to msg & my plural(u, "s were", " was") set msg to msg & " updated" if m > 0 then set msg to msg & "," & return & (m as string) & " item" set msg to msg & my plural(m, "s were", " was") set msg to msg & " not located" end if if s > 0 then set msg to msg & "," & return & (s as string) & " item" set msg to msg & my plural(s, "s", "") set msg to msg & " needed no action" end if set msg to msg & "." display dialog msg with title "FixLinks" giving up after 300 end if end tell -- check that a file exists at a given path on fileExists(theFile) -- (String) as Boolean tell application "System Events" if exists file (my replace(theFile, "/", ":")) then -- display dialog theFile & " exists!" return true else -- display dialog theFile & " does not exist!" return false end if end tell end fileExists -- prompt for media folder location on getMediaPath() set aPath to choose folder with prompt "Select the media folder you wish to find missing tracks in" default location path to music folder set aPath to POSIX path of aPath return aPath end getMediaPath -- return plural or singular form dependent on V on plural(V, p, s) if V = 1 then return s else return p end if end plural -- find : Text to be found -- replace : Text to replace with -- someText : Text to be searched on replace(someText, find, replace) set prevTIDs to text item delimiters of AppleScript set text item delimiters of AppleScript to find set someText to text items of someText set text item delimiters of AppleScript to replace set someText to "" & someText set text item delimiters of AppleScript to prevTIDs return someText end replace -- Replace characters that are invalid in file paths and trim any spaces on safeFile(someText, isFile) -- optionally truncate filename when something other than iTunes/Music has organized files set theLen to my maxLen if theLen > 9 then -- catch errors with really small setting if isFile then set theLen to theLen - 4 if length of someText > theLen then set someText to text 1 thru theLen of someText end if end if -- trim spaces and replace characters set someText to trim(someText) set someText to my replace(someText, "/", "_") set someText to my replace(someText, "?", "_") set someText to my replace(someText, "<", "_") set someText to my replace(someText, ">", "_") set someText to my replace(someText, "\\", "_") set someText to my replace(someText, ":", "_") set someText to my replace(someText, "*", "_") set someText to my replace(someText, "|", "_") set someText to my replace(someText, "\"", "_") end safeFile -- Additional replacements for folders only on safePath(someText) if someText begins with " " then set someText to "_" & text 2 thru -1 of someText if someText begins with "." then set someText to "_" & text 2 thru -1 of someText if someText ends with "." then set someText to text 1 thru -2 of someText & "_" -- custom option for me that needs commenting out -- if someText begins with "The " then set someText to text 5 thru -1 of someText & ", The" set someText to safeFile(someText, false) return someText end safePath -- strip leading and trailing spaces on trim(someText) repeat until someText does not start with " " set someText to text 2 thru -1 of someText end repeat repeat until someText does not end with " " set someText to text 1 thru -2 of someText end repeat return someText end trim