Rename Source Media.fh_lua

--[[
@Title: Rename Source Media 
@Type: Source-driven data entry
@SubType: all
@Author: Calico Pie
@Version: 1.4
@GH #46 #58 #58-2
@Keywords: 
@LastUpdated: September 2020
@Description: Caution: this plugin can potentially rename media files that are external to the current project.  If you have multiple Family Historian projects that store links to the same media files, 
this might not be what you want.

Like all source-driven data entry plugins, this plugin is designed primarily to be run from the Citation Window when viewing a prepared citation to a Source record.  
It is intended to make it easy to ensure consistent naming for associated media.  It renames the Media records and usually also the media files that are associated with the Source record or 
the prepared citation, so that their names match the Source record title, as closely as possible.  It will add a numeric suffix to ensure uniqueness if necessary.

Bear in mind that each media item is represented within Family Historian by a Media record which in turn stores the details of the actual media file.  The media file may be kept either within the project folder or 
externally (you get asked about this when the media is added to the project).  If the media files are kept within the project, the plugin will rename the media files without prompting for confirmation.
However, if the media files are not kept within the project but are external, it will ask for confirmation before renaming them.  Be aware that if a media file is used in other projects as well as the current one, 
if you rename the media file, links to that media file in the other projects will no longer work, unless and until you repair them.

If this plugin is run from the Plugins Window, there may not be a prepared citation.  In that case, it will prompt the user to select the required Source record.  In this case, it will not look at media 
linked to any citations associated with that Source record, prepared or otherwise.
]]

fhInitialise(7,0,0)
fh = require ("fhUtils")

-------------------------------------------------------
-- Main
-------------------------------------------------------

function main()
    local ptrSource
	 local iSourceMedia, iCitationMedia = 0,0
    local pCite = fh.loadPreparedCitation()
    if not(pCite) or not(pCite.source) or pCite.source:IsNull() then
        -- Running standalone prompt for Source
        ptrSource = fhPromptUserForRecordSel('SOUR',1)[1]
    else
        -- Prepared Citation - tidy citation media as well
        local sTitle = cleanTitle(fhGetDisplayText(pCite.source))
        iCitationMedia  = countMedia(fhGetItemPtr(pCite.ptr,'~.SOUR'),'Cite~ '..sTitle)
        ptrSource = pCite.source:Clone()
        
    end
    if ptrSource and ptrSource:IsNotNull() then
        local sTitle = cleanTitle(fhGetDisplayText(ptrSource))
 local mediaFolder = fhGetContextInfo('CI_PROJECT_DATA_FOLDER')..'\\'
	 -- Check if total path exceeds 260, assume the media folder is used
	 local nPathLen = string.len(mediaFolder) + 15
	 local nTitleLen  = string.len(sTitle)
    local nMax  = 260 - nPathLen
	 if (nPathLen + nTitleLen) > 260 then
			local field = {
                {tag="filename", dr="FIELNAME", label="Media Name",type="STRING",value=sTitle, protect=false, maxlength = nMax, minlength  = 1}
			   }
			local r = fh.getParam('Edit Filename','The file name exceeds the maximum available length of '..nMax..'. Please shorten the media title', field,{'OK','Cancel'})
			if r.ok then
				sTitle = r.results.filename
			else
				return
			end
	 end        
        iSourceMedia = countMedia(fhGetItemPtr(ptrSource),sTitle)
		  i = iSourceMedia + iCitationMedia
        local sText
        if i == 1 then
            sText = ('1 media item will be updated')
        elseif i == 0 then
            sText = ('No media items found')
			  fhMessageBox(sText,'MB_OK','MB_ICONINFORMATION')
			  return
        else
            sText = (i..' media items will be updated')
        end
		  sText = sText.." to\n"..sTitle.."\n\nDo you want to continue?"	
		  if fh.yes(sText) then	
	        i = doMedia(fhGetItemPtr(ptrSource),sTitle)
           if iCitationMedia > 0 then
				i = doMedia(fhGetItemPtr(pCite.ptr,'~.SOUR'),'Cite~ '..sTitle)
			  end

		  end
    end
end

function countMedia(ptrBase, sTitle)
    local i = 0
    ptrMedia = fhGetItemPtr(ptrBase,'~.OBJE')
    while ptrMedia and ptrMedia:IsNotNull() do
        i = i + 1
        ptrMedia:MoveNext('SAME_TAG')
    end
    return i
end


function doMedia(ptrBase, sTitle)
    local i = 0
    ptrMedia = fhGetItemPtr(ptrBase,'~.OBJE')
    while ptrMedia and ptrMedia:IsNotNull() do
        -- print(fhGetDisplayText(ptrMedia))
        renameMedia(ptrMedia,sTitle)
        i = i + 1
        ptrMedia:MoveNext('SAME_TAG')
    end
    return i
end

function splitFilename(strFilename)
    -- Returns the Path, Filename, and Extension as 3 values
    return string.match(strFilename, "(.-)([^\\/]-%.?([^%.\\/]*))$")
end

function fileExists(strFileName)
    local fileHandle = io.open(strFileName,"r")
    if fileHandle ~= nil then
        io.close(fileHandle)
        return true
    else
        return false
    end
end

function cleanTitle(sTitle)
    local tblList = {'\\','/',':','*','?','|','>','<','"'}
    for i, v in ipairs(tblList) do
        sTitle = sTitle:gsub(v, "~")
    end
	 sTitle = sTitle:gsub("~+", "~")
    return sTitle
end

function renameMedia(ptrMedia, sTitle)
    local mediaFolder = fhGetContextInfo('CI_PROJECT_DATA_FOLDER')..'\\'
    local root = ''
    local sAsk = ' is not located in the Media folder do you still want to rename it?'
    local bRename = false
    local ptrFile = fhNewItemPtr()
    ptrFile:MoveTo(ptrMedia,'~>FILE')
    local oldName = fhGetValueAsText(ptrFile)
    local path,name,ext = splitFilename(oldName)
    if path:sub(1,5) == 'Media' then
        root = mediaFolder
        bRename = true
    end

    local i = 0
    if bRename or fh.yes(name..sAsk) then

        local newName = path..sTitle..'.'..ext
        while fileExists(root..newName) do
            i = i + 1
            newName = path..sTitle..' ('..i..').'..ext
            -- print(newName)
        end
        local bOK,err = os.rename(root..oldName, root..newName)
		  if bOK then 
          fhSetValueAsText(ptrFile,newName)
		  else
			fhMessageBox('Unable to rename file to '..root..newName.." it may exceed the maximum length for your version of Windows\n\nThe media title only will be changed")
		  end
    end
    -- Update Title of Media Object
    ptrFile:MoveTo(ptrFile,'~.TITL')
    local sNewTitle
    if i > 0 then
        sNewTitle = sTitle..' ('..i..')'
    else
        sNewTitle = sTitle
    end
    fhSetValueAsText(ptrFile,sNewTitle)
end

main()

Source:Rename-Source-Media-4.fh_lua