Best practice using fx Dropbox to stop eternal, manual relinking
Moderators: Víctor Paredes, Belgarath, slowtiger
Best practice using fx Dropbox to stop eternal, manual relinking
Hi,
New Forum member here. I'm a Swedish animation producer developing a series for the Danish public broadcaster DR with Danish director Esben Toft Jacobsen and production company Tall and Small in Copenhagen.
We're working remotely syncing the material via Dropbox, using both Mac and PC. Linked files like BGs (PNG) and animatic files (mp4) can't remember the relative position to the moho-file, and everytime someone else needs to open a shot to do a fix, a render or work on it in other ways, we need to manually relink the files.
My thought is that Moho is looking for the files using absolute file positions, and this is different for each computer - not just between Mac and PC. I can't find any mentions to this issue or how to work around/solve it either here in the Forum or when just plain searching the web.
Does anyone have some light to shed on this and how to solve it?
For production an idea could be that remote users log in to a computer in the studio, working with files on the studio server, but it's not optimal.
Hoping the community can help me find a solution.
Best regards,
Petter
New Forum member here. I'm a Swedish animation producer developing a series for the Danish public broadcaster DR with Danish director Esben Toft Jacobsen and production company Tall and Small in Copenhagen.
We're working remotely syncing the material via Dropbox, using both Mac and PC. Linked files like BGs (PNG) and animatic files (mp4) can't remember the relative position to the moho-file, and everytime someone else needs to open a shot to do a fix, a render or work on it in other ways, we need to manually relink the files.
My thought is that Moho is looking for the files using absolute file positions, and this is different for each computer - not just between Mac and PC. I can't find any mentions to this issue or how to work around/solve it either here in the Forum or when just plain searching the web.
Does anyone have some light to shed on this and how to solve it?
For production an idea could be that remote users log in to a computer in the studio, working with files on the studio server, but it's not optimal.
Hoping the community can help me find a solution.
Best regards,
Petter
Swedish film producer with a passion for animation productions
Email: petter.lindblad@snowcloud.se
Email: petter.lindblad@snowcloud.se
- synthsin75
- Posts: 10253
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Re: Best practice using fx Dropbox to stop eternal, manual relinking
The shared file, and it's external assets, should probably be saved using Gather Media. This is designed to keep track of the linked files relative to the .moho file.
I haven't tried it over file sharing, like Dropbox, though, but I think Greenlaw has.
I haven't tried it over file sharing, like Dropbox, though, but I think Greenlaw has.
- Wes
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
Re: Best practice using fx Dropbox to stop eternal, manual relinking
We sync and share projects trough Nextcloud. Even mix Windows and Mac machines. The way Moho handles file paths is terrible for this because they are almost never relative but absolute (and this needs to be addressed!), but I scripted a solution that makes it work. You do need to run a script each time you open an unlinked file and first tell it not to locate images and there’s a bit more to consider. I’ll share more next week when I’m back at a computer.
Re: Best practice using fx Dropbox to stop eternal, manual relinking
Edit: ignore this post and read the next one. I'll leave this here just in case.
I've edited my script a bit to remove certain specifics. Install this as a menu script, save it as "LK_ChangeServerPaths.lua". You'll also need to install this utility file: FO_Utilities.lua
You'll need to edit this part:
What you see here is 5 options for 5 different setups. Make sure the order is consistent. We use 2 different servers, but you might only need to use the Projects paths. Just fill in some nonsense for the Resources paths. (Make sure you have the same amount of entries in each list though, and the order should match. So person 4 is using projectpath 4 and resourcepath 4.)
Make sure you always import images in a 1080p project. (You can always render in 4K or whatever, but import everything in the same dimension or you'll be sorry down the line).
Whenever opening an unlinked broken moho file. Tell it "No" when a file is missing, and check the "Don't ask again" box. Then run this script.
Also, NEVER save a file with a few unlinked images. It will mess up the dimensions and it's impossible to get them back.
When everything is working for you, disable this line in the Run function:
I hope at some point we'll get relative paths (either relative to a moho file's location, even if it means going 'up' a few folders before digging back into the folder structure, or relative to a customizable project(s) destination)
Use this script at your own risk and keep backups. It might resize images on frame 0 and all keys in all actions. We use it on a daily basis (in a mix of windows/mac machines that mount/sync from linux servers and it works flawlessly, but your setup and projects might be completely different.
I've edited my script a bit to remove certain specifics. Install this as a menu script, save it as "LK_ChangeServerPaths.lua". You'll also need to install this utility file: FO_Utilities.lua
Code: Select all
-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************
ScriptName = "LK_ChangeServerPaths"
-- **************************************************
-- General information about this script
-- **************************************************
LK_ChangeServerPaths = {}
function LK_ChangeServerPaths:Name()
return "Change Server Path"
end
function LK_ChangeServerPaths:Version()
return "0.1"
end
function LK_ChangeServerPaths:Description()
return "Change Server Path"
end
function LK_ChangeServerPaths:Creator()
return "Lukas Krepel, Frame Order"
end
function LK_ChangeServerPaths:UILabel()
return "Change Server Path"
end
LK_ChangeServerPaths.projectsServerPaths = {
"A:/", -- * 1
"/Volumes/PROJECTS/", -- * 2
"D:/dropbox/animations/", -- * 3
"/Users/SomeGuy/Dropbox/Projects/", -- * 4
"D:/lalala/", -- * 5
}
LK_ChangeServerPaths.ResourcesServerPaths = {
"B:/", -- * 1
"/Volumes/RESOURCES/", -- * 2
"A:/Dropbox/Resources/", -- * 3
"/Users/SomeGuy/Dropbox/Resources/", -- * 4
"B:/a weird folder name/every pc is different/", -- * 5
}
LK_ChangeServerPaths.choices = {
"Studio (Windows)", -- * 1
"Studio (macOS)", -- * 2
"SomePersonWorkingAtHome (Dropbox)", -- * 3
"AnotherWorkingAtHome (Dropbox)", -- * 4
"YepWorkingAtHome (Dropbox)", -- * 5
}
LK_ChangeServerPaths.choice = 0
LK_ChangeServerPaths.projectsServerPath = nil
LK_ChangeServerPaths.resourcesServerPath = nil
LK_ChangeServerPaths.changedScale = false
function LK_ChangeServerPaths:LoadPrefs(prefs)
self.choice = prefs:GetInt("LK_ChangeServerPaths.choice", 0)
self.projectsServerPath = prefs:GetString("LK_ChangeServerPaths.projectsServerPath", nil)
self.resourcesServerPath = prefs:GetString("LK_ChangeServerPaths.resourcesServerPath", nil)
end
function LK_ChangeServerPaths:SavePrefs(prefs)
prefs:SetInt("LK_ChangeServerPaths.choice", self.choice)
prefs:SetString("LK_ChangeServerPaths.projectsServerPath", self.projectsServerPath)
prefs:SetString("LK_ChangeServerPaths.resourcesServerPath", self.resourcesServerPath)
end
-- **************************************************
-- Offset Amount Dialog
-- **************************************************
local LK_ServerPathDialog = {}
LK_ChangeServerPaths.images = 0
LK_ChangeServerPaths.changedImages = 0
LK_ChangeServerPaths.scripts = 0
LK_ChangeServerPaths.changedScripts = 0
LK_ChangeServerPaths.scaledLayerNames = {}
LK_ChangeServerPaths.undoPrepped = false
LK_ServerPathDialog.SELECTPATH = MOHO.MSG_BASE + 100 -- 100 t/m 199 gereserveerd
function LK_ServerPathDialog:new(moho)
local d = LM.GUI.SimpleDialog("Change project folder:", LK_ServerPathDialog)
local l = d:GetLayout()
d.moho = moho
--
local layer = moho.document:GetSelectedLayer()
--
d.explanation1 = LM.GUI.StaticText("Change the path for files on the Projects server, they will be replaced for every image layer.")
d.projectsServerPathLabel = LM.GUI.DynamicText("LABEL:", 80)
d.projectsServerPathLabel:SetValue("Projects path:")
d.projectsServerPathTextBox = LM.GUI.DynamicText(LK_ChangeServerPaths.projectsServerPaths[LK_ChangeServerPaths.choice], 500)
--
d.resourcesLabel = LM.GUI.DynamicText("LABEL:", 80)
d.resourcesLabel:SetValue("Resources path:")
d.resourcesTextBox = LM.GUI.DynamicText(LK_ChangeServerPaths.ResourcesServerPaths[LK_ChangeServerPaths.choice], 500)
--
l:PushH()
l:AddChild(d.explanation1)
l:Pop() -- New line
l:PushH()
l:AddChild(d.projectsServerPathLabel)
l:AddChild(d.projectsServerPathTextBox)
--
l:Pop() -- New line
l:PushH()
l:AddChild(d.resourcesLabel)
l:AddChild(d.resourcesTextBox)
--
l:Pop() -- New line
--
l:PushH(LM.GUI.ALIGN_CENTER, -1)
for i, string in ipairs(LK_ChangeServerPaths.projectsServerPaths) do
l:AddChild(LM.GUI.Button(LK_ChangeServerPaths.choices[i], self.SELECTPATH + i))
end
l:Pop() -- New line
-- This is where a "Cancel" and "OK" button show up
return d
end
function LK_ServerPathDialog:HandleMessage(msg)
if msg > self.SELECTPATH and msg < (self.SELECTPATH + 99) then
local option = (msg - self.SELECTPATH)
LK_ChangeServerPaths.choice = option
local projectsPath = LK_ChangeServerPaths.projectsServerPaths[option]
local resourcesPath = LK_ChangeServerPaths.ResourcesServerPaths[option]
self.projectsServerPathTextBox:SetValue(projectsPath)
self.resourcesTextBox:SetValue(resourcesPath)
end
end
function LK_ServerPathDialog:OnOK()
--
end
-- **************************************************
-- The guts of this script
-- **************************************************
function LK_ChangeServerPaths:Run(moho)
moho.document:PrepUndo(moho.layer, false)
moho.document:SetDirty()
self.choice = 0 -- DISABLE THIS LINE ONCE EVERYTHING IS WORKING CORRECTLY BY ADDING "--" IN FRONT OF IT
if (self.choice == 0) then
local dlog = LK_ServerPathDialog:new(moho)
if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then
return
end
end
--
local newProjectsPath = LK_ChangeServerPaths.projectsServerPaths[self.choice]
local newResourcesPath = LK_ChangeServerPaths.ResourcesServerPaths[self.choice]
--
self.projectsServerPath = newProjectsPath
self.resourcesServerPath = newResourcesPath
--
newProjectsPath = self:CorrectSlashesPath(newProjectsPath)
newResourcesPath = self:CorrectSlashesPath(newResourcesPath)
--
self.images = 0
self.changedImages = 0
self.scaledLayerNames = {}
--
self.scripts = 0
self.changedScripts = 0
--
self.undoPrepped = false
self.changedScale = false
--
local layers = FO_Utilities:AllLayers(moho)
self.layersDone = {}
for i = 1, #layers do
local layer = layers[i]
-- *** Images
if (layer:LayerType() == MOHO.LT_IMAGE) then
-- * Set Image Quality to high:
LK_Render:SetHighImageQuality(moho, layer)
-- print ("___"..layer:Name())
self.images = self.images + 1
-- *** Projects
local newPath = newProjectsPath
for j, string in ipairs(LK_ChangeServerPaths.projectsServerPaths) do
if not table.contains(self.layersDone, layer) then
if (j ~= LK_ChangeServerPaths.choice) then
local oldPath = LK_ChangeServerPaths.projectsServerPaths[j]
self:ChangeImagePath(moho, layer, oldPath, newPath)
end
end
end
--
if not table.contains(self.layersDone, layer) then
-- *** Resources
local newPath = newResourcesPath
for j, string in ipairs(LK_ChangeServerPaths.ResourcesServerPaths) do
if not table.contains(self.layersDone, layer) then
if (j ~= LK_ChangeServerPaths.choice) then
local oldPath = LK_ChangeServerPaths.ResourcesServerPaths[j]
self:ChangeImagePath(moho, layer, oldPath, newPath)
end
end
end
end
--
end
-- *** Layerscripts
local scriptPath = layer:LayerScript()
if (scriptPath ~= "") then
self.scripts = self.scripts + 1
self:ChangeLayerscriptPath(moho, layer)
end
end
if (self.changedImages > 0 or self.changedScripts > 0) then
local alertMessage = "Changed paths for " .. self.changedImages .. " / " .. self.images .. " images and " .. self.changedScripts .. " / " .. self.scripts .. " layerscripts."
local scaleAlert = nil
if self.changedScale then
scaleAlert = "WARNING, SCALE HAS BEEN CHANGED FOR "..#self.scaledLayerNames.." IMAGES TO FIX SIZE."
local scaleData = "\n"
for i = 1, #self.scaledLayerNames do
-- print (i.."/"..#self.scaledLayerNames)
local line = self.scaledLayerNames[i]
-- print (line)
scaleData = scaleData..line.."\n"
end
LM.GUI.Alert(LM.GUI.ALERT_WARNING, alertMessage, scaleAlert, scaleData, "OK", nil, nil)
else
LM.GUI.Alert(LM.GUI.ALERT_INFO, alertMessage, scaleAlert, nil, "OK", nil, nil)
end
end
end
-- **************************************************
-- Changing the path on an image layer
-- **************************************************
function LK_ChangeServerPaths:ChangeImagePath(moho, layer, oldPath, newPath)
-- print (" - ChangeImagePath:"..layer:Name())
oldPath = self:CorrectSlashesPath(oldPath)
local imageLayer = moho:LayerAsImage(layer)
local imagePath = (imageLayer:SourceImage())
imagePath = string.gsub(imagePath, oldPath, newPath)
-- * Check if path has changed:
if (imagePath == imageLayer:SourceImage()) then
return
end
-- * Check if file exists:
if not FO_Utilities:FileExists(moho, imagePath) then
print ("Unable to locate image file for layer ["..layer:Name().."] File doesn't exist: ["..imagePath.."]")
return
end
-- print (layer:Name() .. " -> NEW IMAGE PATH: " .. imagePath)
-- * Check old size:
local oldHeight = imageLayer:Height()
-- * Change image source:
if not self.undoPrepped then
moho.document:PrepUndo(moho.layer, true)
moho.document:SetDirty()
self.undoPrepped = true
end
imageLayer:SetSourceImage(imagePath)
-- * Compare to new size:
local newHeight = imageLayer:Height()
-- * Check if difference is significant:
local heightDifference = math.abs (oldHeight - newHeight)
if heightDifference < 0.1 then
heightDifference = 0
end
-- * Change size on frame 0 and timeline:
if heightDifference ~= 0 then --newHeight ~= oldHeight then
self.changedScale = true
local heightChange = oldHeight / newHeight
local scaleChannel = imageLayer.fScale
local oldScale = scaleChannel:GetValue(0)
local newScale = oldScale * heightChange
-- * Main timeline scale channel for this image layer:
local endKey = scaleChannel:Duration()
local thisKey = 0
local keyCount = scaleChannel:CountKeys()
local keysFound = 0
local frameNum = endKey
while keysFound < keyCount do
thisKey = scaleChannel:GetClosestKeyID(frameNum)
keyFrameNum = scaleChannel:GetKeyWhen(thisKey)
keysFound = 1 + keysFound
-- * Scale it:
local oldScale = scaleChannel:GetValue(keyFrameNum)
local newScale = oldScale * heightChange
scaleChannel:SetValue(keyFrameNum, newScale)
frameNum = keyFrameNum - 1
end
-- * Do all action scale channels for this image layer too:
for i = 0, scaleChannel:CountActions()-1 do
local actionChannel = moho:ChannelAsAnimVec3(scaleChannel:Action(i))
local endKey = actionChannel:Duration()
local thisKey = 0
local keyCount = actionChannel:CountKeys()-1 -- * -1 because we don't want to modify frame 0 in actions!
local keysFound = 0
local frameNum = endKey
while keysFound < keyCount do
thisKey = actionChannel:GetClosestKeyID(frameNum)
keyFrameNum = actionChannel:GetKeyWhen(thisKey)
keysFound = 1 + keysFound
-- * Scale it:
local oldScale = actionChannel:GetValue(keyFrameNum)
local newScale = oldScale * heightChange
actionChannel:SetValue(keyFrameNum, newScale)
frameNum = keyFrameNum - 1
end
end
table.insert(self.scaledLayerNames, "- "..layer:Name().." S.old: "..oldScale.y.." S.new: "..newScale.y.." H.diff: "..heightDifference)
end
self.changedImages = self.changedImages + 1
table.insert(self.layersDone, layer)
end
-- **************************************************
-- Changing the path on an image layer
-- **************************************************
function LK_ChangeServerPaths:ChangeLayerscriptPath(moho, layer)
local userPath = string.gsub(moho:UserAppDir(), '\\', '/')
local scriptsFolder = "/Shared Resources/Embedded Scripts/"
local newPath = userPath .. scriptsFolder
newPath = self:CorrectSlashesPath(newPath)
local scriptPath = layer:LayerScript()
local embeddedscript = string.gsub(scriptPath, '\\', '/')
local lastslashpos = (embeddedscript:reverse()):find("%/") -- find last slash
embeddedscript = (embeddedscript:sub(-lastslashpos+1)) -- filename only
oldPath = string.gsub(scriptPath, embeddedscript, "") -- path without filename
oldPath = self:CorrectSlashesPath(oldPath)
-- TODO: ALSO CHECK IF LAYERSCRIPT EXISTS IN NEW LOCATION
if (oldPath ~= newPath) then
scriptPath = string.gsub(scriptPath, oldPath, newPath) -- ***
Debug:Log(layer:Name() .. " -> NEW LAYERSCRIPT PATH: " .. newPath .. embeddedscript)
if not self.undoPrepped then
moho.document:PrepUndo(moho.layer, true)
moho.document:SetDirty()
self.undoPrepped = true
end
layer:SetLayerScript(scriptPath)
self.changedScripts = self.changedScripts + 1
end
end
function LK_ChangeServerPaths:CorrectSlashesPath(path)
if (FO_Utilities:getOS() == "unix") then
path = string.gsub(path, "\\", "/")
else
path = string.gsub(path, "/", "\\")
end
return path
end
-- **************************************************
-- Change a single path
-- (Don't use this when looping trough layers)
-- (Used in other scripts like LK_RenderLocal)
-- **************************************************
function LK_ChangeServerPaths:FixPath(moho, path, choice)
choice = choice or self.choice
-- *** Projects
local newProjectsPath = self.projectsServerPaths[choice]
local newPath = newProjectsPath
for i, string in ipairs(self.projectsServerPaths) do
if not table.contains(self.layersDone, layer) then
if (i ~= choice) then
local oldPath = self.projectsServerPaths[i]
path = string.gsub(path, oldPath, newPath)
end
end
end
local newResourcesPath = self.ResourcesServerPaths[choice]
-- *** Resources
local newPath = newResourcesPath
for i, string in ipairs(self.ResourcesServerPaths) do
if (i ~= choice) then
local oldPath = self.ResourcesServerPaths[i]
path = string.gsub(path, oldPath, newPath)
end
end
return path
end
Code: Select all
LK_ChangeServerPaths.projectsServerPaths = {
"A:/", -- * 1
"/Volumes/PROJECTS/", -- * 2
"D:/dropbox/animations/", -- * 3
"/Users/SomeGuy/Dropbox/Projects/", -- * 4
"D:/lalala/", -- * 5
}
LK_ChangeServerPaths.ResourcesServerPaths = {
"B:/", -- * 1
"/Volumes/RESOURCES/", -- * 2
"A:/Dropbox/Resources/", -- * 3
"/Users/SomeGuy/Dropbox/Resources/", -- * 4
"B:/a weird folder name/every pc is different/", -- * 5
}
LK_ChangeServerPaths.choices = {
"Studio (Windows)", -- * 1
"Studio (macOS)", -- * 2
"SomePersonWorkingAtHome (Dropbox)", -- * 3
"AnotherWorkingAtHome (Dropbox)", -- * 4
"YepWorkingAtHome (Dropbox)", -- * 5
}
Make sure you always import images in a 1080p project. (You can always render in 4K or whatever, but import everything in the same dimension or you'll be sorry down the line).
Whenever opening an unlinked broken moho file. Tell it "No" when a file is missing, and check the "Don't ask again" box. Then run this script.
Also, NEVER save a file with a few unlinked images. It will mess up the dimensions and it's impossible to get them back.
When everything is working for you, disable this line in the Run function:
Code: Select all
self.choice = 0 -- DISABLE THIS LINE ONCE EVERYTHING IS WORKING CORRECTLY BY ADDING "--" IN FRONT OF IT
Use this script at your own risk and keep backups. It might resize images on frame 0 and all keys in all actions. We use it on a daily basis (in a mix of windows/mac machines that mount/sync from linux servers and it works flawlessly, but your setup and projects might be completely different.
Last edited by Lukas on Thu Apr 28, 2022 12:27 pm, edited 1 time in total.
Re: Best practice using fx Dropbox to stop eternal, manual relinking
Here's an easier to use version of the script. Ignore the previous post and use this instead. Install this as a menu script, save it as "LK_ChangeServerPaths.lua". You'll also need to install this utility file: FO_Utilities.lua
Edit this part and make sure you have each different location in there that anyone on the project is using. All should point to the place where you're stuff is stored. For example a 'Projects' folder. It assumes everything is stored somewhere in that location, also the moho files itself.
Tell it not to locate files when opening a shot with unlinked stuff. And run the script.
Let me know what works and what doesn't. We use it in production all the time, but your project setup might be completely different.
Code: Select all
-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************
ScriptName = "LK_ChangeServerPaths"
-- **************************************************
-- General information about this script
-- **************************************************
LK_ChangeServerPaths = {}
function LK_ChangeServerPaths:Name()
return "Change Server Path"
end
function LK_ChangeServerPaths:Version()
return "0.2"
end
function LK_ChangeServerPaths:Description()
return "Change Server Path"
end
function LK_ChangeServerPaths:Creator()
return "Lukas Krepel, Frame Order"
end
function LK_ChangeServerPaths:UILabel()
return "Change Server Path"
end
LK_ChangeServerPaths.serverPaths = {
"Y:/",
"/Volumes/Projects/",
"D:/Dropbox/Projects/",
"/Users/Crazyguy/Dropbox/Projects/",
"D:/Projects/",
"/Users/Bart/Dropbox/Projects_local/",
"E:/MySyncedLocation/"
}
LK_ChangeServerPaths.changedScale = false
-- **************************************************
-- Offset Amount Dialog
-- **************************************************
local LK_ServerPathDialog = {}
LK_ChangeServerPaths.images = 0
LK_ChangeServerPaths.changedImages = 0
LK_ChangeServerPaths.scripts = 0
LK_ChangeServerPaths.changedScripts = 0
LK_ChangeServerPaths.scaledLayerNames = {}
LK_ChangeServerPaths.undoPrepped = false
-- **************************************************
-- The guts of this script
-- **************************************************
function LK_ChangeServerPaths:Run(moho)
local newServerPath = nil
for i = 1, #self.serverPaths do
local serverPath = self:CorrectSlashesPath(self.serverPaths[i])
local docPath = moho.document:Path()
if string.match(docPath, serverPath) then
newServerPath = serverPath
end
end
if newServerPath == nil then
FO_Utilities:Alert("Can't determine current server path.", "Did you save your file on the server?", "?")
print ("Possible server locations:")
for i = 1, #self.serverPaths do
print (" - '"..self.serverPaths[i].."'")
end
return
end
--
moho.document:PrepUndo(moho.layer, false)
moho.document:SetDirty()
--
newServerPath = self:CorrectSlashesPath(newServerPath)
-- print ("newServerPath = "..newServerPath)
--
self.images = 0
self.changedImages = 0
self.scaledLayerNames = {}
--
self.scripts = 0
self.changedScripts = 0
--
self.undoPrepped = false
self.changedScale = false
--
local layers = FO_Utilities:AllLayers(moho)
self.layersDone = {}
for i = 1, #layers do
local layer = layers[i]
-- *** Images
if (layer:LayerType() == MOHO.LT_IMAGE) then
-- * Set Image Quality to high:
LK_Render:SetHighImageQuality(moho, layer)
-- print ("___"..layer:Name())
self.images = self.images + 1
-- * Server
local newPath = newServerPath
for j, string in ipairs(LK_ChangeServerPaths.serverPaths) do
if not table.contains(self.layersDone, layer) then
if not string.match(newServerPath, LK_ChangeServerPaths.serverPaths[j]) then
local oldPath = LK_ChangeServerPaths.serverPaths[j]
self:ChangeImagePath(moho, layer, oldPath, newPath)
end
end
end
end
-- * Layerscripts
local scriptPath = layer:LayerScript()
if (scriptPath ~= "") then
self.scripts = self.scripts + 1
self:ChangeLayerscriptPath(moho, layer)
end
end
if (self.changedImages > 0 or self.changedScripts > 0) then
local alertMessage = "Changed paths for " .. self.changedImages .. " / " .. self.images .. " images and " .. self.changedScripts .. " / " .. self.scripts .. " layerscripts."
local scaleAlert = nil
if self.changedScale then
scaleAlert = "WARNING, SCALE HAS BEEN CHANGED FOR "..#self.scaledLayerNames.." IMAGES TO FIX SIZE."
local scaleData = "\n"
for i = 1, #self.scaledLayerNames do
-- print (i.."/"..#self.scaledLayerNames)
local line = self.scaledLayerNames[i]
-- print (line)
scaleData = scaleData..line.."\n"
end
FO_Utilities:Alert(alertMessage, scaleAlert, scaleData)
else
FO_Utilities:Alert(alertMessage, scaleAlert)
end
else
FO_Utilities:Alert("Nothing changed.", "i")
end
end
-- **************************************************
-- Changing the path on an image layer
-- **************************************************
function LK_ChangeServerPaths:ChangeImagePath(moho, layer, oldPath, newPath)
-- print (" - ChangeImagePath:"..layer:Name())
oldPath = self:CorrectSlashesPath(oldPath)
local imageLayer = moho:LayerAsImage(layer)
local imagePath = (imageLayer:SourceImage())
imagePath = string.gsub(imagePath, oldPath, newPath)
-- * Check if path has changed:
if (imagePath == imageLayer:SourceImage()) then
return
end
-- * Check if file exists:
if not FO_Utilities:FileExists(moho, imagePath) then
print ("Unable to locate image file for layer ["..layer:Name().."] File doesn't exist: ["..imagePath.."]")
return
end
-- print (layer:Name() .. " -> NEW IMAGE PATH: " .. imagePath)
-- * Check old size:
local oldHeight = imageLayer:Height()
-- * Change image source:
if not self.undoPrepped then
moho.document:PrepUndo(moho.layer, true)
moho.document:SetDirty()
self.undoPrepped = true
end
imageLayer:SetSourceImage(imagePath)
-- * Compare to new size:
local newHeight = imageLayer:Height()
-- * Check if difference is significant:
local heightDifference = math.abs (oldHeight - newHeight)
if heightDifference < 0.1 then
heightDifference = 0
end
-- * Change size on frame 0 and timeline:
if heightDifference ~= 0 then --newHeight ~= oldHeight then
self.changedScale = true
local heightChange = oldHeight / newHeight
local scaleChannel = imageLayer.fScale
local oldScale = scaleChannel:GetValue(0)
local newScale = oldScale * heightChange
-- * Main timeline scale channel for this image layer:
local endKey = scaleChannel:Duration()
local thisKey = 0
local keyCount = scaleChannel:CountKeys()
local keysFound = 0
local frameNum = endKey
while keysFound < keyCount do
thisKey = scaleChannel:GetClosestKeyID(frameNum)
keyFrameNum = scaleChannel:GetKeyWhen(thisKey)
keysFound = 1 + keysFound
-- * Scale it:
local oldScale = scaleChannel:GetValue(keyFrameNum)
local newScale = oldScale * heightChange
scaleChannel:SetValue(keyFrameNum, newScale)
frameNum = keyFrameNum - 1
end
-- * Do all action scale channels for this image layer too:
for i = 0, scaleChannel:CountActions()-1 do
local actionChannel = moho:ChannelAsAnimVec3(scaleChannel:Action(i))
local endKey = actionChannel:Duration()
local thisKey = 0
local keyCount = actionChannel:CountKeys()-1 -- * -1 because we don't want to modify frame 0 in actions!
local keysFound = 0
local frameNum = endKey
while keysFound < keyCount do
thisKey = actionChannel:GetClosestKeyID(frameNum)
keyFrameNum = actionChannel:GetKeyWhen(thisKey)
keysFound = 1 + keysFound
-- * Scale it:
local oldScale = actionChannel:GetValue(keyFrameNum)
local newScale = oldScale * heightChange
actionChannel:SetValue(keyFrameNum, newScale)
frameNum = keyFrameNum - 1
end
end
table.insert(self.scaledLayerNames, "- "..layer:Name().." S.old: "..oldScale.y.." S.new: "..newScale.y.." H.diff: "..heightDifference)
end
self.changedImages = self.changedImages + 1
table.insert(self.layersDone, layer)
end
-- **************************************************
-- Changing the path on an image layer
-- **************************************************
function LK_ChangeServerPaths:ChangeLayerscriptPath(moho, layer)
local userPath = string.gsub(moho:UserAppDir(), '\\', '/')
local scriptsFolder = "/Shared Resources/Embedded Scripts/"
local newPath = userPath .. scriptsFolder
newPath = self:CorrectSlashesPath(newPath)
local scriptPath = layer:LayerScript()
local embeddedscript = string.gsub(scriptPath, '\\', '/')
local lastslashpos = (embeddedscript:reverse()):find("%/") -- find last slash
embeddedscript = (embeddedscript:sub(-lastslashpos+1)) -- filename only
oldPath = string.gsub(scriptPath, embeddedscript, "") -- path without filename
oldPath = self:CorrectSlashesPath(oldPath)
-- TODO: ALSO CHECK IF LAYERSCRIPT EXISTS IN NEW LOCATION
if (oldPath ~= newPath) then
scriptPath = string.gsub(scriptPath, oldPath, newPath) -- ***
Debug:Log(layer:Name() .. " -> NEW LAYERSCRIPT PATH: " .. newPath .. embeddedscript)
if not self.undoPrepped then
moho.document:PrepUndo(moho.layer, true)
moho.document:SetDirty()
self.undoPrepped = true
end
layer:SetLayerScript(scriptPath)
self.changedScripts = self.changedScripts + 1
end
end
function LK_ChangeServerPaths:CorrectSlashesPath(path)
if (FO_Utilities:getOS() == "unix") then
path = string.gsub(path, "\\", "/")
else
path = string.gsub(path, "/", "\\")
end
return path
end
-- **************************************************
-- Change a single path
-- (Don't use this when looping trough layers)
-- (Used in other scripts like LK_RenderLocal)
-- **************************************************
function LK_ChangeServerPaths:FixPath(path, newServerPath, docPath)
if newServerPath == nil then
if docPath == nil then
print ("need docPath")
return
end
for i = 1, #self.serverPaths do
local serverPath = self:CorrectSlashesPath(self.serverPaths[i])
if string.match(docPath, serverPath) then
newServerPath = serverPath
end
end
end
if newServerPath ~= nil then
for i, string in ipairs(self.serverPaths) do
if not table.contains(self.layersDone, layer) then
if not string.match(path, newServerPath) then
local oldPath = self.serverPaths[i]
path = string.gsub(path, oldPath, newServerPath)
end
end
end
end
return path
end
Code: Select all
LK_ChangeServerPaths.serverPaths = {
"Y:/",
"/Volumes/Projects/",
"D:/Dropbox/Projects/",
"/Users/Crazyguy/Dropbox/Projects/",
"D:/Projects/",
"/Users/Bart/Dropbox/Projects_local/",
"E:/MySyncedLocation/"
}
Let me know what works and what doesn't. We use it in production all the time, but your project setup might be completely different.
Re: Best practice using fx Dropbox to stop eternal, manual relinking
Hi Lukas,
Thank you for the input and suggestion on how to create a work-around. We're gearing up for a production where we'll have several physical hubs/studios and a lot of remote artists on a longer project, and it seems a bit complicated way to go about it if the number of setups go up and artists coming and leaving the project.
I think I'll reach out to Lost Marble and here if there is a plan to add relative paths in the upcoming half-year that might help us. And I'll do a small test with local machines and having artists remote in to a workstation in one physical location. The latter would have all machines working against the same server and paths would be the same for everyone. Might be less smooth, but worth a test.
Best.
Petter
Thank you for the input and suggestion on how to create a work-around. We're gearing up for a production where we'll have several physical hubs/studios and a lot of remote artists on a longer project, and it seems a bit complicated way to go about it if the number of setups go up and artists coming and leaving the project.
I think I'll reach out to Lost Marble and here if there is a plan to add relative paths in the upcoming half-year that might help us. And I'll do a small test with local machines and having artists remote in to a workstation in one physical location. The latter would have all machines working against the same server and paths would be the same for everyone. Might be less smooth, but worth a test.
Best.
Petter
Swedish film producer with a passion for animation productions
Email: petter.lindblad@snowcloud.se
Email: petter.lindblad@snowcloud.se
Re: Best practice using fx Dropbox to stop eternal, manual relinking
I use 'import by reference' all the time and relink to OTHER assets by breaking the links.
For now it's a manual labor.
In Autodesk Maya, '.ma' is 3D-file but also a text format where you can find and replace assets in the text-file.
So in one action, I can replace 3D-assets with other assets in the 3D-scene
Is there a way in Moho to relink assets with a script? Or is there another way?
Is this script the start of such a thing?
Luc
For now it's a manual labor.
In Autodesk Maya, '.ma' is 3D-file but also a text format where you can find and replace assets in the text-file.
So in one action, I can replace 3D-assets with other assets in the 3D-scene
Is there a way in Moho to relink assets with a script? Or is there another way?
Is this script the start of such a thing?
Luc
Lukas wrote: ↑Tue Apr 26, 2022 12:24 pm Edit: ignore this post and read the next one. I'll leave this here just in case.
I've edited my script a bit to remove certain specifics. Install this as a menu script, save it as "LK_ChangeServerPaths.lua". You'll also need to install this utility file: FO_Utilities.lua
Code: Select all
-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** [/quote]
Re: Best practice using fx Dropbox to stop eternal, manual relinking
I don't normally work directly out of Dropbox but here's' a word of warning:
I read in another thread that a user had trouble properly saving Moho files to Dropbox, and recently I saw this issue myself. Here's the problem:
If you have Autosave enabled, there's a bug where Moho will not properly save files to a Dropbox directory, and this can lead to corrupted files or lost data. In some cases, it's must a matter of changing the file's extension back to .moho but there's no guarantee that you will recover the latest version of your file.
There are two things you can do right now:
1. If you disable Autosave, Moho seems to save the file correctly. So, if you choose this approach, make sure all your artists have Autosave disabled.
2. IMO, the safest approach for the time being is to use Dropbox for archival/sharing of Moho projects, but have the artists work with a copy of a project located outside of the Dropbox. (Working on my own network independently of Dropbox is what I do anyway, but I also like to leave Autosave enabled.) When they are finished, the updated copy can be copied back into the shared Dropbox folder.
Hope this helps.
NEW! Visit our Little Green Dog Channel on YouTube!
D.R. Greenlaw
Artist/Partner - Little Green Dog
Little Green Dog Channel | Greenlaw's Demo Reel Channel
D.R. Greenlaw
Artist/Partner - Little Green Dog
Little Green Dog Channel | Greenlaw's Demo Reel Channel