Saturday, September 10, 2022

How to Programmatically Add Tags to Files in OS X on a Mac Using C# Without Libraries

While working on photos, I wanted to programmatically add tags to photos. The tags would be read from a file. I formally used Finder to do this, but it can be tedious for a large universe of tags and there's no record of the association. Here's my solution which is really awkward and brittle. Surely there is a better way. Please add your comments on that way. Until then, I'll use this code.
using System;
using System.Diagnostics;

namespace ImageDescriptions
{
    public class Utilities
    {
        public static string? executeShellCommand(string scriptFile, string arguments)
        {
            var processInfo = new ProcessStartInfo()
            {
                FileName = scriptFile,
                Arguments = arguments,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                CreateNoWindow = true
            };
            string? result = string.Empty;
            Process process = Process.Start(processInfo);   // Start that process.
            while (!process.StandardOutput.EndOfStream)
            {
                result = process.StandardOutput.ReadLine();
                System.Console.WriteLine("result: " + result);
            }
            process.WaitForExit();
            return result;
        }
        /// 
        /// Sets tags on a file in OS X.
        /// This is really awkward and brittle.
        /// 
        /// Comma separated list, e.g., "red,black,green"
        /// file to which we add tags
        public static void SetTags(string? tagCsv, string filename)
        {
            System.Console.WriteLine($"SetTags: '{tagCsv}', '{filename}'");
            var tagXmlString = string.Empty;
            if (tagCsv == null || String.IsNullOrWhiteSpace(tagCsv)) { return; }
            var tags = tagCsv.Split(',');
            foreach (var tag in tags)
            {
                tagXmlString += "" + tag.Trim() + "";
            }
            var scriptFile = "/usr/bin/xattr";
            var arguments = " -w"
               + " com.apple.metadata:_kMDItemUserTags"
               + @" """
               + @" "
               + tagXmlString
               + @" """
               + " " + filename;
            System.Console.WriteLine($"executeShellCommand: {scriptFile}, {arguments}");

            Utilities.executeShellCommand(scriptFile, arguments);
        }
    }
}

Here's a program that reads a data file and then calls "SetTags()":
using ImageDescriptions;

public class ImageDescription
{
    public static void Main(string[] args)
    {
        if(args.Length < 1)
        {
            Console.WriteLine("usage: ImageDescription ");
            Environment.Exit(1);
        }
        string filename = args[0];
        FileStream file = File.OpenRead(filename);

        var lines = File.ReadLines(filename);
        foreach (var line in lines)
        {
            Description description = new Description(line);
            Console.WriteLine(line);
            Console.WriteLine(description);
            Utilities.SetTags(description.tags, description.name);
        }
    }
}

A line from the data file looks like this:
2013-01-23-0702-IMG_8252.jpg "Dessert pie with fruit -tags:dessert,fruit,healthy,pie"

No comments: