// Assets written to XML must be copied to the Metadata folder, and a thumbnail is produced.
        public static void copyAsset(asset ass)
        {
            if (Helpers.staticIsImageFile(ass.path))
            {
                String dataDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Data\\";
                String newPath = dataDir + "Images/Metadata/" + ass.uniqueName;
                try
                {
                    // First, copy the asset.
                    File.Delete(newPath);
                    File.Copy(ass.path, newPath);

                    // Then create a thumbnail.
                    System.Drawing.Image thumb = System.Drawing.Image.FromFile(ass.path);
                    thumb = thumb.GetThumbnailImage(128, 128, null, new IntPtr());
                    thumb.Save(Path.GetDirectoryName(newPath) + "/" + "Thumbnail/" + ass.uniqueName);
                    thumb.Dispose();
                }
                catch (Exception e) {
                    throw new InvalidCSVArtworkException("Error copying image asset at " + ass.path + ". Message: " + e.Message);
                }
            }
            else if (Helpers.staticIsVideoFile(ass.path))
            {
                String newPath = "Data/Videos/Metadata/" + Path.GetFileNameWithoutExtension(ass.uniqueName) + ".bmp";
                try
                {
                    // First, copy the asset.
                    File.Delete(newPath);
                    File.Copy(ass.path, newPath);

                    // Then create a thumbnail, using the thumbnail image of the video.
                    string bmpName = Path.GetFileNameWithoutExtension(ass.path) + ".bmp";
                    System.Drawing.Image thumb = System.Drawing.Image.FromFile(bmpName);
                    thumb.Save(newPath);
                    thumb.Dispose();
                }
                catch (Exception e) {
                    throw new InvalidCSVArtworkException("Error copying video asset at " + ass.path);
                }
            }
        }
 // Validates an asset and generates a unique name for it.
 // An asset is invalid if:
 // - Its title is missing.
 // - Its path is not an existing image file.
 public static void validateAsset(asset ass)
 {
     // Convert the asset path to an absolute path.
     // If it is relative, it's relative to the CSV directory.
     if (!Path.IsPathRooted(ass.path))
     {
         ass.path = Path.GetDirectoryName(inputCSVPath) + "\\" + ass.path;
     }
     if (String.IsNullOrWhiteSpace(ass.name))
         throw new InvalidCSVArtworkException("Asset name missing.");
     if (String.IsNullOrWhiteSpace(ass.path) || !Helpers.staticIsImageFile(ass.path) || !File.Exists(ass.path))
         throw new InvalidCSVArtworkException("Asset path is not an existing image file.");
     ass.uniqueName = generateUniqueName(ass.path, "Data/Images/Metadata/");
 }
        // Parses a single asset.
        public static asset parseAsset(String field)
        {
            // First take out the description, because it has the special ''' delimiter.
            // Break the input into:
            // [path and name] [description] [extraneous semicolon]
            string[] descSeparator = new string[] {"'''"};
            string[] tokens1 = field.Split(descSeparator, StringSplitOptions.None);
            if (tokens1.Length < 3) throw new InvalidCSVArtworkException("Unable to parse description.  Did you include all required fields?");
            // Then split the other two fields.
            string[] tokens2 = tokens1[0].Split(';');

            if (tokens2.Length < 2) throw new InvalidCSVArtworkException("Unable to parse path or title.  Did you include all required fields?");
            asset ass = new asset();
            ass.description = tokens1[1];
            ass.path = tokens2[0];
            ass.name = tokens2[1];
            return ass;
        }