Jump to: Board index » General » Fusion

Save project with assets or Collect Files

Learn about 3D compositing, animation, broadcast design and VFX workflows.
  • Author
  • Message
Offline

Emir Bojorquez

  • Posts: 8
  • Joined: Mon Nov 17, 2014 3:34 am

Save project with assets or Collect Files

PostMon Dec 01, 2014 4:59 pm

Hello.

Is there an option to save my fusion project so i can move the folder with my project and it's assets to other systems and no get the "Missing Files" message. In some 3D programs is called "Save project with assets", in After effects is called "Collect Files".

Thank you
Offline
User avatar

Pieter Van Houte

  • Posts: 642
  • Joined: Wed Nov 05, 2014 1:04 am

Re: Save project with assets or Collect Files

PostMon Dec 01, 2014 10:13 pm

I think there is an archive script that comes with Fusion. If not I'll dig it out. :)
Support We Suck Less on Patreon -> https://www.patreon.com/wesuckless

https://www.steakunderwater.com/wesuckless
Offline

Emir Bojorquez

  • Posts: 8
  • Joined: Mon Nov 17, 2014 3:34 am

Re: Save project with assets or Collect Files

PostTue Dec 02, 2014 2:50 am

You are very right, the Script "Archive Composition" is what i was looking for.

Thank you
Offline
User avatar

Bernhard Rieder

  • Posts: 115
  • Joined: Tue Aug 04, 2015 6:49 pm

Re: Save project with assets or Collect Files

PostSun Jul 02, 2017 9:05 pm

Where can I download that script?
How to install and how to use it?

Thank you!
Offline
User avatar

Andrew Hazelden

  • Posts: 536
  • Joined: Sat Dec 06, 2014 12:10 pm
  • Location: West Dover, Nova Scotia, Canada

Re: Save project with assets or Collect Files

PostMon Jul 03, 2017 9:44 am

Here is a copy of the "Archive Composition.lua" script that has been updated to work with Fusion 7 - Fusion 8.2.1 on Windows/macOS/Linux.

Code: Select all
------------------------------------------------------------------------------
-- Archive Composition, Revision: 2.1
--
-- composition script
--
-- This script scans every loader and saver for clips, then copies those clips to
-- a specified destination. Fonts used in Text tools are also included. Size is
-- precalculated so that you can be certain
-- enough space exists for the composition and footage at the destination before
-- proceeding.
--
-- written by : Isaac Guenard (izyk@eyeonline.com), sean konrad (sean@eyeonline.com)
-- written    : July 4th, 2003

-- updated : Sept 27, 2005
-- changes : updated for 5

-- updated : Oct 4, 2008
-- changes : fixed bugs in handling of MOV files and stills. Added support for archiving fonts and fbx meshes

-- updated: 2016-12-19 9.22 AM by Andrew Hazelden (andrew@andrewhazelden.com)
-- From the We Suck Less Thread:
-- https://www.steakunderwater.com/wesuckless/viewtopic.php?f=6&t=1111
-- changed : TIME_UNDEFINED to fu.TIME_UNDEFINED
-- changed : direxists(dir) function to work with Fusion 8
-- changed : GetNextCompositionSave() function to work Fusion 8 and on macOS/Linux
-- changed : folder paths and directory creation code to be cross platform compatible
-- added : createdir(dir) function
-- added : fu_major_version variable
-- added : operating system platform check and an os_separator variable
-- added : alembic mesh support
-- Todo: Use bmd.scriptlib to check if a clip is a movie so there isn't a dummy frame number added to the movie filename before the frame extension

-- updated: 2017.06.12 - Pieter Van Houte - v2.1
-- changed: incorporated SV_GetFrames from bmd.scriptlib and adjusted to work with Fu8+
-- changed: replaced eyeon.SV_GetFrames() with SV_GetFrames()

-- WISH LIST
-- standalone version?
-- make prints support multiframe better
-- saver needs to handle multiframe formats better (currently prints 1 frame)
-- output a todo script instead of immediate mode copy
-- Make it ignore orphaned Loaders?
-- Handle Fuses and Plugins
-- Maybe even handle imported camera paths?
-- preserve original file structure
-- save file containing date and time into folder so you can tell when it was archived. Maybe allow extra notes?
-- dump error log to text file

-- make a trimmed files only mode
-- compression option for zip files
------------------------------------------------------------------------------


----------------------------------------------------------------------
-- exit if this is not a composition script
----------------------------------------------------------------------
if composition == nil then
   print("This is a composition script, it should be run from within the Digital Fusion interface.")
   exit()
end

----------------------------------------------------------------------
-- Find out the current operating system platform. (Andrew Hazelden Edit)
-- The platform local variable should be set to either 'Windows', 'Mac', or 'Linux'.
----------------------------------------------------------------------
-- Find out if we are running Fusion 7 or 8
local fu_major_version = math.floor(tonumber(eyeon._VERSION))

local platform = ''
local os_separator = ''
if string.find(comp:MapPath('Fusion:\\'), 'Program Files', 1) then
  -- Check if the OS is Windows by searching for the Program Files folder
  platform = 'Windows'
  os_separator = '\\'
elseif string.find(comp:MapPath('Fusion:\\'), 'PROGRA~1', 1) then
  -- Check if the OS is Windows by searching for the Program Files folder
  platform = 'Windows'
  os_separator = '\\'
elseif string.find(comp:MapPath('Fusion:\\'), 'Applications', 1) then
  -- Check if the OS is Mac by searching for the Applications folder
  platform = 'Mac'
  os_separator = '/'
else
  platform = 'Linux'
  os_separator = '/'
end

------------------------------------------------------------------------------
--      DECLARE FUNCTIONS                                                   --
------------------------------------------------------------------------------


------------------------------------------------------------------------------
-- FUNCTION createdir()
-- Creates a new folder using the operating system provided native calls
------------------------------------------------------------------------------

function createdir(dir)
  if platform == 'Windows' then
    os.execute('mkdir "' .. dir ..'"')
  else
    -- Mac and Linux
    os.execute('mkdir -p "' .. dir ..'"')
  end
end
   
------------------------------------------------------------------------------
-- FUNCTION direxists()
--
-- masks the real readdir, handles the path to eliminate trailing \
-- deals with files named same as directory trying to create
------------------------------------------------------------------------------
function direxists(dir)
   local val = false
   local x = string.sub(dir, -1, -1)
   
   if (x == "\\") or (x == "/") then
      str = string.sub(dir, 1, -2)
   else
      str = dir
   end
   
   -- Fusion 8.x compatibility fix: Commented out the lua-fs related readdir() call
   -- local res = readdir(str)
   -- if res then   val = res[1].IsDir end
   
  if fu_major_version >= 8 then
    -- The script is running on Fusion 8+ so we will use the fileexists command
    if eyeon.fileexists(dir) then
      val = true
    end
  else
    -- The script is running on Fusion 6/7 so we will use the direxists command
    if eyeon.direxists(dir) then
      val = true
    end
  end
 
   
   return val
end

------------------------------------------------------------------------------
-- FUNCTION filesize()
-- return the size of the file named in the src_path string, or 0 and an error
------------------------------------------------------------------------------
function filesize(src_path)
   src, errMsg = io.open(src_path, "rb")
   if src == nil then
      return 0, "SOURCE : "..errMsg
   end
   local size = src:seek("end")
   src:close()
   
   return size, errMsg
end

------------------------------------------------------------------------------
-- FUNCTION compare_clip()
-- used by buildClipList() to ensure that loaders pointing at the same media are not
-- added to the cliplist twice.
-- WARNING requires global variable 'cliplist'
------------------------------------------------------------------------------
function compare_clip(seq)
   for index, item in pairs(cliplist) do
      if item.Seq.Path == seq.Path then
         if item.Multiframe == 1 then
            if item.Seq.FullName == seq.FullName then
               return index
            end
         else
            if item.Seq.CleanName..item.Seq.Extension == seq.CleanName..seq.Extension then
               return index
            end
         end
      end
   end
   return nil
end

------------------------------------------------------------------------------
-- FUNCTION buildClipList()
-- assembles a table of values useful for manipulating every clip in the composition
-- WARNING requires global variable 'cliplist'
------------------------------------------------------------------------------
function buildClipList(ld)
   local attrs    = ld:GetAttrs()
   local isduplicate
   
   -- what if we wanted to archive even passed through tools?
   --tool passed through
   if attrs.TOOLB_PassThrough == true then
      return
   end
   
   --loader not set up
   if attrs.TOOLST_Clip_Name == nil then
      return
   end
   
   -- if its a loader
   for i = 1, table.getn(attrs.TOOLST_Clip_Name) do
      
      local seq = eyeon.parseFilename(composition:MapPath(attrs.TOOLST_Clip_Name[i]))
      clip = {}
      clip.ClipName      = attrs.TOOLST_Clip_Name[i]
      clip.Number         = seq.Number
      clip.ImportMode      = attrs.TOOLIT_Clip_ImportMode[i]
      clip.Start          = attrs.TOOLNT_Clip_Start[i]
      clip.TrimIn       = attrs.TOOLIT_Clip_TrimIn[i]
      clip.TrimOut       = attrs.TOOLIT_Clip_TrimOut[i]
      clip.Loader         = ld
      
      -- do we have this clip already?
      isduplicate = compare_clip(seq)

      if isduplicate == nil then
         files = {}
         files.Seq         = seq
         files.InitialFrame= attrs.TOOLIT_Clip_InitialFrame[i]
         files.Length       = attrs.TOOLIT_Clip_Length[i]
         files.Multiframe   = attrs.TOOLBT_Clip_IsMultiFrame[i]
         files.Clip         = {clip}
         table.insert(cliplist, files)
      else
         table.insert(cliplist[isduplicate].Clip, clip)
      end
   end
   return
end


function GetNextCompositionSave()
   usual_path = string.lower(fusion:MapPath("Comps:"..os_separator))
   x, y = string.find(usual_path, ":")
   
   
   if x == nil then
      -- may not be a valid path, or may be a unc name
      
      
      if fu_major_version >= 8 then
      -- The script is running on Fusion 8+ so we will use the fileexists command
      if eyeon.fileexists(usual_path) then
        path = usual_path
      end
    else
      -- The script is running on Fusion 6/7 so we will use the direxists command
      if eyeon.direxists(usual_path) then
        path = usual_path
      end
    end
   
    -- fallback to using the user's home folder if the absolute path doesn't exist
    if path == nil then
      if platform == 'Windows' then
           path = os.getenv("USERPROFILE")..os_separator..string.sub(usual_path, x)
         else
            -- macOS/ Linux
            path = os.getenv("HOME")..os_separator..string.sub(usual_path, x)
         end
      end
   else
      -- may be a virtual using fusion: or temp:
      virtual = string.sub(usual_path, 1, x)
      
      if virtual == "fusion:" then
         path = getfilepath(fusion:GetAttrs().FUSIONS_FileName)..string.sub(usual_path, x+1)
      elseif virtual == "temp:" then
        if platform == 'Windows' then
           path = os.getenv("TEMP")..os_separator..string.sub(usual_path, x+1)
         else
           -- macOS/ Linux 
           -- note: $TMPDIR has a trailing slash built in like /var/folders/h9/y47405113bqb9v343fl5rxlr0000gn/T/
           path = os.getenv("TMPDIR")..string.sub(usual_path, x+1)
         end
      else
         path = usual_path
      end
   end
   return path .. composition:GetAttrs().COMPS_Name .. ".comp"   
end


------------------------------------------------------------------------------
-- SV_GetFrames(sv)
--
-- This function takes a Saver and returns a table containing the names of
-- the frames the saver will actually output. If the tool provided is not a
-- saver or the saver has never been set up then the return value is nil.
--
------------------------------------------------------------------------------
function SV_GetFrames(sv)
   
   local fla = sv.Composition:GetAttrs()
   
   if sv.ID ~= "Saver" then
      return nil, "The tool "..sv.Name.." is not a Saver tool."
   end
   
   if sv.Normal[fu.TIME_UNDEFINED] == 1 then
      return nil, sv.Name.." is set to 2:3 pulldown. This function does not support pulldown."
   end
   
   -- its safe to assume [0] for Clipname since savers have no cliplists
   local sv_file = sv.Clip[0]

   if sv_file == "" then
      return nil, sv.Name.." does not yet have a filename to save to."
   end
   
   -- multiframe clips only have one filename
   if pathIsMovieFormat (sv.Clip[0]) == true then
      return {sv_file}
   end
   
   local seq = bmd.parseFilename(sv_file)
   
   -- Saver has a control to force the starting sequence number.
   if sv.SetSequenceStart[fu.TIME_UNDEFINED] == 0 then
      start = fla.COMPN_RenderStart
   else
      start = sv.SequenceStartFrame[fu.TIME_UNDEFINED]
               + fla.COMPN_RenderStart
               - fla.COMPN_GlobalStart
   end
   
   local length = fla.COMPN_RenderEnd - fla.COMPN_RenderStart

   if seq.Padding == nil then
      -- never rendered, no numbering provided assume default fusion padding
      seq.Padding = 4
   end
      
   local files = {}
   for i = start, start + length do
      table.insert(files, seq.Path..seq.CleanName..string.format("%0"..seq.Padding.."d", i)..seq.Extension)
   end
   return files

end


------------------------------------------------------------------------------
-- MAIN BODY
--
--
--
------------------------------------------------------------------------------
total_size    = 0
size      = 0
badframe    = {}
cliplist   = {}
errText      = ""

------------------------------------------------------------------------------
-- If the composition is not saved, offer a chance to save the composition, or exit
------------------------------------------------------------------------------
while composition:GetAttrs().COMPS_FileName == "" do
   ret = composition:AskUser("Error", {
      {"save_path", Name="save composition to", "FileBrowse", Default=GetNextCompositionSave(), Save=true},
      {"description", "Text", Lines=5, Default="Please save the composition before running this script. You can use the path dialog above to save the composition, or Cancel to exit.", ReadOnly=true, Wrap = true}
      })
   if ret == nil then
      return
   else
      composition:Save(ret.save_path)
   end
end

lds = comp:GetToolList(false, "Loader")
svs = comp:GetToolList(false, "Saver")
txt = comp:GetToolList(false, "TextPlus")
txt3d = comp:GetToolList(false, "Text3D")
fbx = comp:GetToolList(false, "SurfaceFBXMesh")
abc = comp:GetToolList(false, "SurfaceAlembicMesh")

------------------------------------------------------------------------------
-- Allow the user to choose initial options for the script, including
-- pre-calculate filesize for total copy, and to select whether savers are included
------------------------------------------------------------------------------
init = composition:AskUser("Archive Composition", {
   {"analyze", Name="Calculate Total Size", "Checkbox", Default=1, NumAcross=2},
   {"savers", Name="Include Savers", "Checkbox", Default=1, NumAcross=2},
   {"fbx", Name="Include FBX", "Checkbox", Default=1, NumAcross=2},
   {"abc", Name="Include Alembic", "Checkbox", Default=1, NumAcross=2},
   {"fonts", Name="Include Fonts", "Checkbox", Default=1, NumAcross=2},
   {"instructions", Name="Instructions", "Text", Default="This script will collect all the clips used by your composition into folders beneath a single root directory. A copy of the composition will also be saved in the destination, with all loaders pointing to the new clip locations.\n\nThe script will calculate total file sizes before copying so that you can ensure enough space is available at the destination. You may disable the filesize pass by deselecting the checkbox above.", Wrap=true, Lines=10, ReadOnly=true}
   })

if init == nil then return end

------------------------------------------------------------------------------
-- LOADERS CLIPLIST
--
-- assemble the loader cliplist
------------------------------------------------------------------------------
for i, ld in pairs(lds) do
   buildClipList(ld)
end

------------------------------------------------------------------------------
-- SAVERS CLIPLIST
--
-- assemble the saver cliplist. If savers are not included then we just have an empty table
------------------------------------------------------------------------------
err = ""
sv_cliplist = {}
if init.savers == 1 then
   for i, sv in pairs(svs) do
   
      files, errText = SV_GetFrames(sv)
      if (files == nil) or ( table.getn(files) == 0 )then
         err = err..errText.."\n"
      else
         table.insert(sv_cliplist, {sv, files})
      end
      
   end
end

if err ~= "" then
   ret = composition:AskUser("Warning", {
   {"description", "Text", Lines=10, Default="One or more Saver tools will be ignored. Tool names and reasons are listed below. Click OK to continue or Cancel to exit the script.\n\n"..err, ReadOnly=true, Wrap=true}
   })
   if ret == nil then return end
end
err = ""
errText = ""


------------------------------------------------------------------------------
-- FONT LIST
--
------------------------------------------------------------------------------
font_files = {}

-- skip this if there are no text tools
if table.getn(txt) == 0 and table.getn(txt3d) == 0 then
   init.fonts = 0
end

if init.fonts == 1 then
   local fontmanager = fusion.FontManager
   local fontlist = fontmanager:GetFontList()

   for i, t in pairs(txt) do
      local font = t.Font[fu.TIME_UNDEFINED]
      local style = t.Style[fu.TIME_UNDEFINED]
      
      font_files[ fontlist[ font ][ style ] ] = true
   end
   
   for i, t in pairs(txt3d) do
      local font = t.Font[fu.TIME_UNDEFINED]
      local style = t.Style[fu.TIME_UNDEFINED]
      
      font_files[ fontlist[ font ][ style ] ] = true
   end   
end

------------------------------------------------------------------------------
-- FBX MESHES
--
------------------------------------------------------------------------------
fbx_files = {}

if init.fbx == 1 then
   for i, t in pairs(fbx) do
      if not fbx_files[ t.ImportFile[fu.TIME_UNDEFINED] ] then
         fbx_files[ t.ImportFile[fu.TIME_UNDEFINED] ] = {t}
      else
         table.insert( fbx_files[ t.ImportFile[fu.TIME_UNDEFINED] ], t)
      end
      
   end
end

------------------------------------------------------------------------------
-- ABC MESHES
--
------------------------------------------------------------------------------
abc_files = {}

if init.fbx == 1 then
   for i, t in pairs(abc) do
      if not abc_files[ t.Filename[fu.TIME_UNDEFINED] ] then
         abc_files[ t.Filename[fu.TIME_UNDEFINED] ] = {t}
      else
         table.insert( abc_files[ t.Filename[fu.TIME_UNDEFINED] ], t)
      end
      
   end
end


------------------------------------------------------------------------------
-- CALCULATE FILESIZES
--
-- if the option to pre-calculate file size was selected, go ahead and do it
------------------------------------------------------------------------------
if init.analyze == 1 then
   
   -- font filesizes
   for i, v in pairs(font_files) do
      total_size = total_size + filesize(i)
   end
   
   -- fbx filesizes
   for i, v in pairs(fbx_files) do
      total_size = total_size + filesize(i)
   end
   
   -- abc filesizes
   for i, v in pairs(abc_files) do
      total_size = total_size + filesize(i)
   end

   -- loader filesizes
   for i, v in pairs(cliplist) do
   
      if v.Multiframe == 1 then
         total_size = total_size + filesize(v.Seq.FullPath)
      else
         -- is it a still
         if (v.Seq.Padding == nil) and (v.Length == 1) then
            total_size = total_size + filesize(v.Seq.FullPath)
         else
            for i = v.InitialFrame, v.InitialFrame + (v.Length-1) do
               fname = v.Seq.CleanName..string.format("%0"..v.Seq.Padding.."d", i)..v.Seq.Extension
               total_size = total_size + filesize(v.Seq.Path..fname)
            end         
         end
      end
   end

   -- saver filesizes
   for i, clip in pairs(sv_cliplist) do
      for i, file in pairs(clip[2]) do
         total_size = total_size + filesize(file)
      end
   end

   local res = table.getn(cliplist)+table.getn(sv_cliplist).." clips total at "..string.format("%.2f", total_size/1048576).." MB."
   ret = composition:AskUser("Composition - Total File Size", {
      {"result", "Text", Name="Total Filesize For Composition", Default=res, ReadOnly=true, Lines=1},
      {"instructions", "Text", Name="Instructions", Default="Click OK to continue, or Cancel to exit.", ReadOnly=true}
      })
   --
   if ret == nil then return end
end

-- need to add fonts and fbx to filesize calculation

--------------------------------------------
-- reset total size to avoid double counting
total_size = 0



------------------------------------------------------------------------------
-- CHOOSE DESTINATION FOLDER
--
--  select the ultimate destination folder for the archived composition
------------------------------------------------------------------------------
msg = "Select a destination folder, All footage in your composition will be copied to subfolders of this directory."
errText = ""

--------------------------------------------
-- keep displaying the dialog until they get a valid path, or they cancel the script
gotpath = false

while gotpath == false do

  -- Destination Directory
  --local defaultPath = comp:MapPath('UserDocs:'..os_separator..'Archive Composition'..os_separator)
  local defaultPath = comp:MapPath('Comp:'..os_separator..'Archive Composition'..os_separator)
  -- local defaultPath = 'C:\\eyeon\\projects\Archive Composition\\destination\\'

   ret = composition:AskUser("Archive Composition", {
      {"root", Name="specify a destination directory", "PathBrowse", Save=true, Default=defaultPath},
      {"instructions", Name="About this script", "Text", Default= errText .. msg, Wrap=true, Lines=10}
      })
   
   if ret == nil then return end
   
   --------------------------------------------
   -- no entry made? ask again
   if ret.root == "" then
      errText = "Error: You must provide a root directory for the script to copy files to.\n\n"
      gotpath = false
   else
      --------------------------------------------
      -- manually entered paths may not have a slash at the end. provide one
      if string.sub(ret.root, -1, -1) ~= os_separator then ret.root = ret.root .. os_separator end

      
      --------------------------------------------
      -- createdir returns false if it fails, and false if the directory exists
      -- so we create the directory, and then see if it really is there.
      createdir(ret.root)
      
      -- is the directory there?
      gotpath = direxists(ret.root)
      errText = "Error: Could not create directory "..ret.root.."\n\n"
      
   end
end

--------------------------------------------
-- I use ret a lot for dialogs, so get the important
-- information into a more stable variable
output_root = ret.root


--------------------------------------------
-- now that the new dir exists
-- save a copy of the composition in the root folder of the destination directory
output_composition = output_root..composition:GetAttrs().COMPS_Name
composition:Save(output_composition)

print()
print("-----------------------------------")
print("-- Archiving Composition         --")
print("-----------------------------------")


------------------------------------------------------------------------------
--  COPY ALL FONTS
--
-- 
------------------------------------------------------------------------------
if init.fonts == 1 then
   print()
   print("Copy Fonts  ")
   print("------------\n")

   -- create folder for the fonts
   new_dir = output_root.."Fonts"..os_separator
   createdir(new_dir)
   
   for font_path, val in pairs(font_files) do
      fseq = eyeon.parseFilename(font_path)
      
      print(font_path)
      
      size, errText = eyeon.copyfile(font_path, new_dir..fseq.FullName)
   
      if size == 0 then
         table.insert(badframe, errText)
      end
      
      total_size = total_size + size
   end
end


------------------------------------------------------------------------------
--  COPY ALL FBX FILES
--
--  if two fbx files with the same name came from different directories this
--  function would overwrite one of them.
------------------------------------------------------------------------------
if init.fbx == 1 then
   print()
   print("Copy FBX + OBJ Meshes")
   print("------------\n")
   
   -- create folder for the fbx files
   new_dir = output_root.."Meshes"..os_separator
   virtual_dir = "Comp:"..os_separator.."Meshes"..os_separator
   
   createdir(new_dir)
   
   for mesh, val in pairs(fbx_files) do
      mesh_seq = eyeon.parseFilename(mesh)
      
      print(mesh)
      size, errText = eyeon.copyfile(mesh, new_dir..mesh_seq.FullName)
      
      if size == 0 then
         table.insert(badframe, errText)
      end
      
      total_size = total_size + size
      
      comp:Lock()
      for i, tool in pairs(val) do
         tool.ImportFile[fu.TIME_UNDEFINED] = virtual_dir .. mesh_seq.FullName
      end
      comp:Unlock()
   end
   
   
end



------------------------------------------------------------------------------
--  COPY ALL ABC FILES
--
--  if two abc files with the same name came from different directories this
--  function would overwrite one of them.
------------------------------------------------------------------------------
if init.abc == 1 then
   print()
   print("Copy ABC Meshes")
   print("------------\n")
   
   -- create folder for the abc files
   new_dir = output_root.."Meshes"..os_separator
   virtual_dir = "Comp:"..os_separator.."Meshes"..os_separator
   
   createdir(new_dir)
   
   for mesh, val in pairs(abc_files) do
      mesh_seq = eyeon.parseFilename(mesh)
      
      print(mesh)
      size, errText = eyeon.copyfile(mesh, new_dir..mesh_seq.FullName)
      
      if size == 0 then
         table.insert(badframe, errText)
      end
      
      total_size = total_size + size
      
      comp:Lock()
      for i, tool in pairs(val) do
         tool.Filename[fu.TIME_UNDEFINED] = virtual_dir .. mesh_seq.FullName
      end
      comp:Unlock()
   end
   
   
end


------------------------------------------------------------------------------
--  COPY ALL LOADER CLIPS
--
-- 
------------------------------------------------------------------------------
print()
print("Copy Loaders")
print("------------\n")

index = 1
for i, clip in pairs(cliplist) do
   seq = clip.Seq
   
   
   --------------------------------------------
   -- different branches for multiframe versus sequence
   if clip.Multiframe == 1 then
   
      --------------------------------------------
      -- before we copy make sure one with the same name does not already exist
      -- an alternate method would be to call fileexists() on filename
      if io.open(output_root..seq.FullName, "r") == nil then
         init_frame = seq.FullName
      else
         init_frame = string.format("%04d", i).."__"..seq.FullName
      end
      
      new_dir = output_root.."Movies"..os_separator
      virtual_dir = "Comp:"..os_separator.."Movies"..os_separator
      
      createdir(new_dir)
      
      print(seq.Name.." : Copying "..string.format("%.2f", filesize(seq.FullPath)/1048576).." MB")
      size, errText = eyeon.copyfile(seq.FullPath, new_dir..init_frame)
      
      if size == 0 then
         table.insert(badframe, errText)
      end
      
      total_size = total_size + size
   else
      if clip.Length == 1 then
         new_dir = output_root.."Stills"..os_separator
         virtual_dir = "Comp:"..os_separator.."Stills"..os_separator
         
         createdir(new_dir)
         
         
         -- does a still file with the same name already exist at this location?
         if fileexists(new_dir..seq.FullName) == true then
            init_frame = string.format("%04d", i).."__"..seq.FullName
         else
            init_frame = seq.FullName
         end
         
         
         print(init_frame.." : Copying "..string.format("%.2f", filesize(seq.FullPath)/1048576).." MB")
         size, errText = eyeon.copyfile(seq.FullPath, new_dir..init_frame)
         if size == 0 then
            table.insert(badframe, errText)
         end
         total_size = total_size + size
      else -- Its a sequence
         d_name = string.format("%04d", index).."__"..seq.CleanName.."_"..seq.Extension
         new_dir = output_root..d_name..os_separator
         virtual_dir = "Comp:"..os_separator..d_name..os_separator
         
         createdir(new_dir)
         
         start = clip.InitialFrame
         theend = start + (clip.Length-1)
         
         init_frame = seq.CleanName..string.format("%0"..seq.Padding.."d", start)..seq.Extension
         
         print(init_frame.." : "..clip.Length.." Frames ")
         
         for i = start, theend do
            fname = seq.CleanName..string.format("%0"..seq.Padding.."d", i)..seq.Extension
            
            size, errText = eyeon.copyfile(seq.Path..fname, new_dir..fname)
            
            if size == 0 then
               table.insert(badframe, errText)
               composition:Print("x")
            else
               composition:Print(".")
            end
            
            total_size = total_size + size
         end
         
         composition:Print("\n")
         index = index + 1
      end
   end
   --------------------------------------------
   -- clip copied, update loaders using that clip
   composition:Lock()
   for index, item in pairs(clip.Clip) do
      item.Loader.Clip[item.Start] = virtual_dir..init_frame
   end
   composition:Unlock()
   
end



------------------------------------------------------------------------------
-- COPY FILES FROM SAVERS
--
-- 
------------------------------------------------------------------------------
print()
print("Copy Savers")
print("------------\n")

for i, s in pairs(sv_cliplist) do
   sv = s[1]
   sva = sv:GetAttrs()
   files = s[2]
   
   d_name = string.format("%04d", i).."___"..sva.TOOLS_Name
   new_dir = output_root..d_name..os_separator
   
   createdir(new_dir)
   
   init_frame = eyeon.getfilename(files[1])
   
   print(sva.TOOLS_Name.." : "..table.getn(files).." Frames ")
   
   for i, file in pairs(files) do
      size, errText = eyeon.copyfile(file, new_dir..eyeon.parseFilename(file).FullName)
      
      if size == 0 then
         table.insert(badframe, errText)
         composition:Print("x")
      else
         composition:Print(".")
      end
      
      total_size = total_size + size
   end
   
   composition:Print("\n")
   
   --------------------------------------------
   -- clip copied, update saver using that clip
   composition:Lock()
   sv.Clip[fu.TIME_UNDEFINED] = "Comp:"..os_separator..d_name..os_separator..init_frame
   composition:Unlock()
end



------------------------------------------------------------------------------
-- PRINT ERRORS THAT OCCURED ALONG THE WAY
--
-- 
------------------------------------------------------------------------------

if table.getn(badframe) == 0 then
   ret = composition:AskUser("Copy Complete", {
      {"results", Name="File Copy Complete", "Text", Default="File copy complete. All clips have been copied to "..ret.root..". "..string.format("%.2f", total_size/1048576).." MB were copied in total.", Lines=3, ReadOnly=true, Wrap=true}
      })
else
   ret = composition:AskUser("Copy Complete", {
      {"results", Name="File Copy Complete", "Text", Default="File copy complete. "..table.getn(badframe).." files were not copied to "..ret.root..". "..string.format("%.2f", total_size/1048576).." MB were copied in total.\n\nClick OK to print a complete list of failed frames to the console, along with reasons for failing.", Lines=6, ReadOnly=true, Wrap=true}
      })
   if ret ~= nil then
      for i, v in pairs(badframe) do
         print(v)
      end
   else
      return
   end
end

-- our work here is done.
print("\nScript Done")
composition:Save(output_composition)
Mac Studio M2 Ultra / Threadripper 3990X | Fusion Studio 18.6.4 | Kartaverse 6
Offline

wutongdegugeyouxiang

  • Posts: 49
  • Joined: Thu Nov 16, 2017 2:00 pm

Re: Save project with assets or Collect Files

PostThu Nov 16, 2017 2:12 pm

上面的收集工程的脚本在win下不支持非utf-8编码的语言,对于用其它语言制作的项目收集不了工程。
DaVinci Resolve 16.1.0b.017;i7 6700, rx 590, 16G RAM
Offline

dedpnda

  • Posts: 40
  • Joined: Mon Jan 22, 2018 6:42 am
  • Real Name: Morgan Mendieta

Re: Save project with assets or Collect Files

PostThu Apr 06, 2023 3:23 pm

Does this collect media? i am trying to archive but i can not get this to collect my media.
Morgan Mendieta

Return to Fusion

Who is online

Users browsing this forum: KrunoSmithy and 22 guests