Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to preserve link to source files after downloading an aaf from the server? #111

Open
Dylan-Buth opened this issue Dec 1, 2022 · 3 comments

Comments

@Dylan-Buth
Copy link

This may be a basic question. I'm pretty new to aaf files in general and am having trouble understanding this part.

We're building an aaf file server-side, from WAV files. We then let the user download a zip that includes the aaf, and corresponding wav files. The problem is, once the user downloads it, the link is wrong. For example, a SourceMob has a locator object, with a urlstring pointing to the absolute path of the wav file on the server. Is there a way to set that locator to a path, relative to the aaf file? (Ex. ./source-wav-1.wav) Or is there another solution to this problem that I'm missing?

Logic Pro seems to handle it best, prompting the user to select the location of files it can't find. Pro Tools just gives an error.

@markreidvfx
Copy link
Owner

markreidvfx commented Dec 1, 2022

The AAF Edit Protocol says relative paths are supported. If pro tools supports them is another story. I'm not entirely sure the syntax, the spec says to follow RFC 2396. Unfortunately I don't have access to pro tools to test.

@bozzochet
Copy link

bozzochet commented Aug 12, 2024

Hi, I try to necro-bump this issue, but I'm facing the same "problem".
I really hate that the AAF produced by Logic Pro has these damn absolute path... It's completely stupid: I'm saving the AAF to pass my project (song) to a guy using another DAW and the best I can do is a damn file not easily usable if moved from the original directory on the original computer used to export... It's a nonsense...

I also hate that Logic Pro is exporting all the wav files in a directory called "Audio Files" (let's start hating the use of the space...). Two songs exported? The second is "Audio Files 2". Completely idiot...

So I wrote a pyaaf2 script to change the AAF file to change this path.

It took a while but I managed to make it work (so now I can move and rename the dir with the wav files where I like) but I ended not being able to "relativify" the paths...

None of the commented ones are working:

path_in = "file:///Users/bozzo/Documents/Musica/GarageBand/NuvolaNera/Audio%20Files"
path_out = "file:///Users/bozzo/Documents/Musica/GarageBand/NuvolaNera/NuvolaNera_Files"
#path_out = "file://NuvolaNera_Files"
#path_out = "file://.NuvolaNera_Files"
#path_out = "file://./NuvolaNera_Files"
#path_out = "./NuvolaNera_Files"

There's something I miss in how I write the path?

Script attached (I had to add .txt...) and full code copied below if someone would need it.
aaf_editor.py.txt

Matteo

import aaf2
import chardet

source_file = "./NuvolaNera.AAF"
dest_file = "./NuvolaNera_edited.AAF"

path_in = "file:///Users/bozzo/Documents/Musica/GarageBand/NuvolaNera/Audio%20Files"
path_out = "file:///Users/bozzo/Documents/Musica/GarageBand/NuvolaNera/NuvolaNera_Files"
#path_out = "file://NuvolaNera_Files"
#path_out = "file://.NuvolaNera_Files"
#path_out = "file://./NuvolaNera_Files"
#path_out = "./NuvolaNera_Files"

path_in_enc = path_in.encode('utf-16le','strict')
path_out_enc = path_out.encode('utf_16le','strict')

def copy_mobs(a, b):
    for mob in a.content.mobs:
        if mob.mob_id not in b.content.mobs:
            # copy the mob from file a into file b
            mob_copy = mob.copy(root=b)
            b.content.mobs.append(mob_copy)

def copy_essence_data(a, b):
    for essence_data in a.content.essencedata:
        if essence_data.mob_id not in b.content.essencedata:
            # copy the essence data from file a into file b
            essence_data_copy = essence_data.copy(root=b)
            b.content.essencedata.append(essence_data_copy)

with aaf2.open(dest_file, "w") as f_dest:
    with aaf2.open(source_file, "r") as f:

        #        f.dump()
        #        f.content.dump()

        f_dest.dictionary.update(f.dictionary)
        
        for mm in f.content.mobs:
            print("name: ", mm.name)
            if ".wav" in mm.name:
                #                mm.dump()
                #                print(mm.keys())
                ed = mm.get("EssenceDescription")
                #                print(type(ed))
                if ed is not None:
                    #                    print("    Ess.Descr. name: ", ed.name)
                    p1d = ed.propertydef
                    #                    print("    Prop.1.Def. name: ", p1d.name)
                    r1 = ed.value
                    #                    print(r1)
                    d1 = ed.data
                    #                    print(d1.decode())
                    #                    print(ed.object.keys())
                    ll = ed.object.get("Locator")
                    #                    print(ll.value)
                    nll = ll.value
                    nl = nll[0]
                    #                    print(nl.keys())
                    us = nl.get("URLString")
                    #                    print(type(us))
                    #                    print(type(us.data))
                    #                    print(us.data)
                    #                    the_encoding = chardet.detect(us.data)['encoding']
                    #                    print(the_encoding)
                    print(us.data.decode())
                    ss = us.data.decode('utf_16le','strict')
                    #                    print(type(ss))
                    #                    print("Len: ", len(ss))
                    ss = ss.replace(path_in, path_out)
                    #                    data_new = us.data.replace(path_in_enc, path_out_enc)
                    #                    ss_new = data_new.decode()
                    #                    print(ss_new)
                    print(ss)
                    us.data=ss.encode('utf_16le','strict')
                    print(mm.get("EssenceDescription").object.get("Locator").value[0].get("URLString").data.decode())
        copy_mobs(f, f_dest)
        copy_essence_data(f, f_dest)

@bozzochet
Copy link

Just for reference.
On the AAF Edit Protocol document, pag 10:

The EssenceDescriptor::Locator property should include at least one NetworkLocator that complies with the following constraints:

  • The NetworkLocator has a URI that complies with RFC 2396.
  • It is an absolute or a relative URI.
  • If it is an absolute URI, it conforms to the RFC1738 file URI scheme.
  • If it is a relative URI, the base URI is determined from the URI of the AAF file itself.
    When exporting an AAF file containing relative URIs, the exporting application should also export the target resources. If this is not possible, absolute URIs should be substituted or appended as additional NetworkLocators.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants