Usage

The main things to know

BinLog

BinLog represents an Avid bin’s history log. It behaves as a list of BinLogEntry objects, and can be used to read, process and write properly-formatted .log files.

BinLogEntry

BinLogEntry is a dataclass which represents one entry in such a log. Per the .log file spec, a BinLogEntry has the following fields:

Field

Purpose

Default Value

BinLogEntry.timestamp

Timestamp of the modification

datetime.datetime.now()

BinLogEntry.computer

Name of the machine that made the modification

defaults.DEFAULT_COMPUTER

BinLogEntry.user

Avid user profile name that made the modification

defaults.DEFAULT_USER

See About Avid bin logs for more information about Avid bin logs.

Creating new bin logs

Although it’s more likely you’ll be working with existing logs, let’s start by making one from scratch, just to get some concepts down.

Creating a new bin log
 1from binhistory import BinLog, BinLogEntry
 2
 3my_kewl_log = BinLog()
 4
 5# Add a BinLogEntry with default values
 6my_kewl_log.append(BinLogEntry())
 7
 8# Add a BinLogEntry with specified `computer` and `user` values
 9my_kewl_log.append(BinLogEntry(computer="zAutomation", user="otto"))
10
11# Let's see those entries
12for entry in my_kewl_log:
13    print(entry)

Here’s the example output:

BinLogEntry(timestamp=datetime.datetime(2025, 3, 9, 16, 5, 59, 112426), computer='zMichael', user='mjordan')
BinLogEntry(timestamp=datetime.datetime(2025, 3, 9, 16, 5, 59, 112437), computer='zAutomation', user='otto')

The first entry uses default values for timestamp, computer, and user. The second entry still uses the default timestamp, but has our custom computer and user values.

Now let’s see how BinLog would format this for the .log file, with BinLog.to_string():

Creating a new bin log (cont’d)
14print(my_kewl_log.to_string())

Output:

Sun Mar 09 16:05:59  Computer: zMichael        User: mjordan
Sun Mar 09 16:05:59  Computer: zAutomation     User: otto

Great! So let’s write this out as a .log for our Reel 1.avb Avid bin with BinLog.to_bin()

Creating a new bin log (cont’d)
15my_kewl_log.to_bin("01_Edits/Reel 1.avb")

Caution

BinLog.to_bin() will overwrite an existing .log

Reading existing logs

BinLog can read an Avid bin’s log with BinLog.from_bin():

classmethod BinLog.from_bin(bin_path: str, missing_bin_ok: bool = True, max_year: int | None = None) BinLog

Load an existing .log file for a given bin

If no .log file exists for the given bin, exceptions.BinLogNotFoundError is raised.

So to read the log for the Avid bin 01_Edits/Reel 1.avb:

 1from binhistory import BinLog
 2from binhistory.exceptions import BinLogNotFoundError
 3
 4bin_path = "01_EDITS/Reel 1.avb"
 5try:
 6    log = BinLog.from_bin(bin_path)
 7except BinLogNotFoundError:
 8    print(f"No log file exist for {bin_path}")
 9    return
10
11for entry in log:
12    print(entry)

Here’s an example output from an Avid bin that has been modified four times:

BinLogEntry(timestamp=datetime.datetime(2024, 8, 29, 17, 27, 12), computer='zJoy', user='joyjoy')
BinLogEntry(timestamp=datetime.datetime(2024, 8, 30, 14, 10, 16), computer='zMichael', user='mjordan')
BinLogEntry(timestamp=datetime.datetime(2024, 8, 30, 14, 16, 42), computer='zMichael', user='mjordan')
BinLogEntry(timestamp=datetime.datetime(2025, 2, 22, 18, 5, 43), computer='zTimmy', user='user')

About Those Timestamps…

The .log file spec does not specify the year in its log entry timestamp format; however it does specify the name of the day of the week (Mon, Tue, Wed).

When parsing each entry from a .log file, the year is inferred by fetching the year of the file modified date of the .log as a starting point, and looking backwards through the years until a valid “day-of-the-week + day-of-the-month” combo is found in the calendar.

This seems to work quite well for active projects, but be aware of this for cases when file modified dates are inaccurate — for example, archived projects that may not have maintained their original file modification dates when they were restored.

In these cases, you can pass a custom year as an int to the max_year argument to override the “file modified year” to get a more accurate date.

Working with logs

As a list

Since BinLog is a python collections.UserList of BinLogEntry objects, you can do all the usual list-y kinds of things.

 1from binhistory import BinLog, BinLogEntry
 2from binhistory.exceptions import BinLogTypeError
 3
 4# Create a log with 5 entries
 5my_log = BinLog([BinLogEntry(), BinLogEntry(), BinLogEntry(), BinLogEntry(), BinLogEntry()])
 6
 7# Get the third entry
 8some_entry = my_log[2]
 9
10# Modify the third entry
11my_log[2] = some_entry.copy_with(computer="zSomething")
12
13# Add a couple more entries
14my_log.extend([BinLogEntry(computer="zNew"), BinLogEntry(user="Another")])
15
16# Count the entries
17print(f"my_log has {len(my_log)} entries.")
18
19# Note: `BinLog` will only accept `BinLogEntry`s
20try:
21    my_log.append("Heehee oops")
22except BinLogTypeError as e:
23    print(f"This didn't work: {e}")

Convenience methods

Often you may be interested in only the earliest or the last entry in the log. Well, buddy, you’re not gonna believe this:

BinLog.earliest_entry() BinLogEntry | None

Get the first/earliest entry from a bin log

BinLog.latest_entry() BinLogEntry | None

Get the last/latest/most recent entry from a bin log

These methods will return None if the BinLog has no BinLogEntrys.

 1from binhistory import BinLog
 2from binhistory.exceptions import BinLogNotFoundError
 3
 4bin_path = "01_EDITS/Reel 1.avb"
 5try:
 6    latest_entry = BinLog.from_bin(bin_path).latest_entry()
 7except BinLogNotFoundError:
 8    print(f"No log file exist for {bin_path}")
 9    return
10
11if not latest_entry:
12    # Sometimes a .log file exists, but is empty
13    print(f"Empty log for {bin_path}")
14    return
15
16print(f"{bin_path} was last modified by {latest_entry.computer} on {latest_entry.timestamp}")

Example output:

01_EDITS/Reel 1.avb was last modified by zTimmy on 2025-02-22 18:05:43

You can also get lists of unique field values in a log with the following:

BinLog.computers() List[str]

Get a list of unique computers in the log

BinLog.users() List[str]

Get a list of unique users in the log

BinLog.timestamps() List[datetime]

Get a list of unique timestamps in the log

As an example, let’s get a list of all the bins that have been recently modified by my zMichael machine. Now we can blame me for things!

 1import pathlib
 2from binhistory import BinLog
 3from binhistory.exceptions import BinLogNotFoundError
 4
 5suspect = "zMichael"
 6
 7# Get all the Avid bins in a project
 8for bin_path in pathlib.Path("/Volumes/Important Avid Project/").rglob("*.avb"):
 9
10    if bin_path.name.startswith("."):
11        # Skip dotfiles
12        continue
13
14    try:
15        # Read the log for this bin
16        log = BinLog.from_bin(bin_path)
17    except BinLogNotFoundError:
18        # Skip bins without logs
19        continue
20
21    if suspect in log.computers():
22        print(f"{suspect} made changes to {bin_path}!")

Working with log entries

As mentioned earlier, a BinLogEntry object is a dataclass that represents a single log entry. It comes with default values set for each of the fields, so let’s check that out first:

1from binlog import BinLogEntry
2
3print(BinLogEntry())

Output:

BinLogEntry(timestamp=datetime.datetime(2025, 3, 8, 15, 27, 25, 76253), computer='zMichael', user='mjordan')

Nice! We see timestamp defaults to the current datetime. computer defaults to the hostname of my machine, and user defaults to the username executing the script.

Note

Those default values can be changed, on a per-script basis, by modifying the constants defined in the defaults module.

Now, those first two are in line with the data Avid would use for the log entry under normal circumstances. But for user, typically Avid would use the name of the current Avid user profile. So let’s specify that field:

1from binlog import BinLogEntry
2
3print(BinLogEntry(user="MJ 2023.12.2"))

Output:

BinLogEntry(timestamp=datetime.datetime(2025, 3, 8, 15, 28, 29, 47132), computer='zMichael', user='MJ 2023.12.2')

Cool. This matches exactly what Avid would normally use for a bin entry.

Writing logs

The BinLog class knows how to write properly-formatted .log files, and provides a couple of ways of doing so.

Note

While BinLog may contain any number of BinLogEntry objects in any order, the resulting .log file will always be output with a maximum of defaults.MAX_ENTRIES of the most recent entries, sorted according to the BinLogEntry.timestamp field.

Touching a bin

BinLog.touch_bin() is a class method which appends an entry to the log of a given Avid bin. It’s designed to be a quick thing you can call with no arguments, but you may optionally provide a custom BinLogEntry

If no .log exists for the Avid bin, one will be created.

classmethod BinLog.touch_bin(bin_path: str, entry: BinLogEntry | None = None, missing_bin_ok: bool = True)

Add an entry to a log file for a given bin

In this example, imagine it’s… oh I don’t know… 3:27pm on March 8, 2025.

1from binhistory import BinLog
2
3bin_path = "01_Edits/Reel 1.avb"
4
5# Add a default BinLogEntry to the log
6BinLog.touch_bin(bin_path)
7
8# Now let's see that log
9print(BinLog.from_bin(bin_path).latest_entry())

The latest entry shows my touch:

BinLogEntry(timestamp=datetime.datetime(2025, 3, 8, 15, 27, 25, 76253), computer='zMichael', user='mjordan')