public static void LogSuccess(string message, DebugStack ds) { if (ds != null) { ds.LogSuccess(message); } else { LogSuccess(message); } }
// --- Main Process Method --- private static TagWriterReturnCode WriteTagsForRow(Entry entry, int processed, DebugStack ds) { TagLib.File file; ds.Log("#" + processed + " - " + entry.fileName); // Check if the entry is mapped to a file if (entry.isMapped == false) { string message = "Entry is not matched to file"; ds.LogWarning(" -> " + message); entryErrors.TryAdd(entry, message); return(TagWriterReturnCode.Unmapped); } bool isRetry = false; Retry: // Create TagLib file representation, catch to see if the file is valid try { file = TagLib.File.Create(entry.mappedFilePath, isRetry ? ReadStyle.Average : ReadStyle.PictureLazy); } catch (Exception exception) { var type = exception.GetType(); string errorMessage = ""; TagWriterReturnCode returnCode; if (type == typeof(UnsupportedFormatException)) { errorMessage = "File format with extension \"" + Path.GetExtension(entry.mappedFilePath) + "\" is unsupported (" + exception.Message + ")"; returnCode = TagWriterReturnCode.UnsupportedFormat; } else if (type == typeof(CorruptFileException)) { errorMessage = "File appears corrupt! " + exception.Message; returnCode = TagWriterReturnCode.CorruptFile; } else if (type == typeof(IOException)) { errorMessage = "An IO exception occured while loading the file! " + exception.Message; // Get the locking processes (if any) var lockingProcesses = FileLockUtility.GetProcessesLockingFile(entry.mappedFilePath); // Log locking processes if (lockingProcesses != null && lockingProcesses.Count > 0) { Debug.LogError("\tThe file appears to be locked by the process(es): " + string.Join(", ", lockingProcesses.Select(p => p.ProcessName).ToArray())); } returnCode = TagWriterReturnCode.IOEError; } else { errorMessage = "An error occured while loading the file! " + exception.Message; returnCode = TagWriterReturnCode.OtherError; } ds.LogError(" -> " + errorMessage); entryErrors.TryAdd(entry, errorMessage); return(returnCode); } // Before doing anything, remove any tags that aren't on the disk. TagTypes tagLibCreatedTags = file.TagTypes ^ file.TagTypesOnDisk; file.RemoveTags(tagLibCreatedTags); // Then create a new tag type if needed TagLibUtility.CreateTagIfRequired(file, out TagTypes usedTagTypes); // Remove the ID3v1 tag if set in the settings at this point if (Settings.Current.removeID3v1 && file.TagTypes.HasFlag(TagTypes.Id3v1)) { file.RemoveTags(TagTypes.Id3v1); } // --- RESUME COPYING OVER FROM HERE --- // If we're in remove move if (args.removeTags) { if (!args.skipDateAdded) { TagLibUtility.RemoveCustomTag(file, Consts.TAG_DATE_ADDED, ds); } if (!args.skipLastPlayed) { TagLibUtility.RemoveCustomTag(file, Consts.TAG_LAST_PLAYED, ds); } if (!args.skipPlayCount) { TagLibUtility.RemoveCustomTag(file, Consts.TAG_PLAY_COUNT, ds); } if (!args.skipRating) { TagLibUtility.RemoveRating(file, ds); } entry.wroteTags = false; } // If we're in writing mode else { // Date Added if (!args.skipDateAdded && entry.dateAdded != DateTime.MinValue) { // Create LDAP/Windows File Time long long dateAddedWinTime = entry.dateAdded.ToFileTime(); if (Settings.Current.fullLogging) { ds.Log(string.Format("\tWriting {0}: {1}\n\t-> Converted to {3} (or {2})", Consts.PLIST_KEY_DATE_ADDED, entry.dateAdded.ToString("o", System.Globalization.CultureInfo.InvariantCulture), entry.dateAdded.ToString(), dateAddedWinTime)); } // Write the tag TagLibUtility.WriteCustomTag(file, Consts.TAG_DATE_ADDED, dateAddedWinTime.ToString(), ds); } // Last Played if (!args.skipLastPlayed && entry.lastPlayed != DateTime.MinValue) { // Create LDAP/Windows File Time long long lastPlayedWinTime = entry.lastPlayed.ToFileTime(); if (Settings.Current.fullLogging) { ds.Log(string.Format("\tWriting {0}: {1}\n\t-> Converted to {3} (or {2})", Consts.PLIST_KEY_LAST_PLAYED, entry.lastPlayed.ToString("o", System.Globalization.CultureInfo.InvariantCulture), entry.lastPlayed.ToString(), lastPlayedWinTime)); } // Write the tag TagLibUtility.WriteCustomTag(file, Consts.TAG_LAST_PLAYED, lastPlayedWinTime.ToString(), ds); } // Play Count if (!args.skipPlayCount && entry.playCount > 0) { if (Settings.Current.fullLogging) { ds.Log(string.Format("\tWriting {0}: {1}", Consts.PLIST_KEY_PLAY_COUNT, entry.playCount)); } // Write the tag TagLibUtility.WriteCustomTag(file, Consts.TAG_PLAY_COUNT, entry.playCount.ToString(), ds); } // Rating if (!args.skipRating && entry.rating != Rating.Unrated) { int ratingInStars = (int)entry.rating; if (Settings.Current.fullLogging) { ds.Log(string.Format("\tWriting {0}: {1} ({2} stars)", Consts.PLIST_KEY_RATING, ratingInStars * 20, ratingInStars)); } // Write the tag TagLibUtility.WriteRating(file, ratingInStars, ds); } // Process WAV files (after writing stats, since an ID3v2 tag should exist then) if (Settings.Current.writeInfoToWavFiles && file.MimeType == "taglib/wav") { // This will compare the file with the entry, and will write any missing info that is present in the entry - but not present in the file. Limited to the following tags: Name, Artist, Album Artist, Album, Genre, Comments, Year, Disc Number, Disc Count, Track Number, Track Count ds.Log("\tWriting WAV info:"); // Create an Id3v2 tag if it doesn't already exist. var id3v2tag = (TagLib.Id3v2.Tag)file.GetTag(TagTypes.Id3v2, true); // Create a RIFF INFO tag if it doesn't already exist // foobar2000 seems to require a RIFF INFO chunk with at least one tag to detect the ID3v2 tags var riffInfo = (TagLib.Riff.InfoTag)file.GetTag(TagTypes.RiffInfo, true); // Write track title if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_TITLE)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_TITLE)) && !string.IsNullOrEmpty(entry.trackTitle)) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_TITLE, entry.trackTitle, ds); //TagLibUtility.WriteTextTag(riffInfo, Consts.RIFF_ID_TITLE, entry.trackTitle, ds); } // Write artist if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_ARTIST)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_ARTIST)) && !string.IsNullOrEmpty(entry.artist)) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_ARTIST, entry.artist, ds); //TagLibUtility.WriteTextTag(riffInfo, Consts.RIFF_ID_ARTIST, entry.artist, ds); } // Write album artist (ID3v2 only) if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_ALBUM_ARTIST)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_ALBUM)) && !string.IsNullOrEmpty(entry.albumArtist)) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_ALBUM_ARTIST, entry.albumArtist, ds); } // Write album if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_ALBUM)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_ALBUM)) && !string.IsNullOrEmpty(entry.album)) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_ALBUM, entry.album); //TagLibUtility.WriteTextTag(riffInfo, Consts.RIFF_ID_ALBUM, entry.album); } // Write genre if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_GENRE)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_GENRE)) && !string.IsNullOrEmpty(entry.genre)) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_GENRE, entry.genre, ds); //TagLibUtility.WriteTextTag(riffInfo, Consts.RIFF_ID_GENRE, entry.genre, ds); } // Write year if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_DATE)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_YEAR)) && !string.IsNullOrEmpty(entry.year)) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_DATE, entry.year, ds); //TagLibUtility.WriteTextTag(riffInfo, Consts.RIFF_ID_YEAR, entry.year, ds); } // Write track number if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_TRACK_NUMBER)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_TRACK_NUMBER)) && entry.trackNumber != null) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_TRACK_NUMBER, entry.trackNumberDisplay, ds); //TagLibUtility.WriteTextTag(riffInfo, Consts.RIFF_ID_TRACK_NUMBER, entry.trackNumberDisplay, ds); } // Write disc number (ID3v2 only) if (string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.ID3v2_FRAME_DISC_NUMBER)) && entry.discNumber != null) { TagLibUtility.WriteTextTag(id3v2tag, Consts.ID3v2_FRAME_DISC_NUMBER, entry.discNumberDisplay, ds); } // Write comments if (string.IsNullOrEmpty(TagLibUtility.GetCommentsTag(file)) && string.IsNullOrEmpty(TagLibUtility.GetTextTag(file, Consts.RIFF_ID_COMMENTS)) && !string.IsNullOrEmpty(entry.comments)) { TagLibUtility.WriteCommentsTag(file, entry.comments, ds); //TagLibUtility.WriteTextTag(riffInfo, Consts.RIFF_ID_COMMENTS, entry.comments, ds); } } entry.wroteTags = true; } // Write the modified tags to the file try { if (Settings.Current.fullLogging) { TagTypes newTags = file.TagTypesOnDisk ^ file.TagTypes; ds.Log("\tTags for writing:\t" + file.TagTypes.ToString()); ds.Log("\t New tags:\t\t" + newTags.ToString()); ds.Log("\t Existing tags:\t" + file.TagTypesOnDisk.ToString()); } if (!Settings.Current.dryRun) { file.Save(); } // If we're here, it means the TagLib.File was saved correctly if (isRetry) { ds.LogSuccess("\tReattempt succeeded"); } } catch (IOException ioex) { // If this was a retry... if (isRetry) { string message = "Could not save file after retry. An exception occurred: " + ioex.Message; ds.LogError("\t" + message); entryErrors.TryAdd(entry, message); return(TagWriterReturnCode.IOEError); } else { ds.LogError("\t-> An IO exception occured while saving tags to the file \"" + entry.fileName + "\""); // Get the locking processes (if any) var lockingProcesses = FileLockUtility.GetProcessesLockingFile(entry.mappedFilePath); // Print the process(es) that are locking the file if (lockingProcesses != null && lockingProcesses.Count > 0) { string message = "The file appears to be locked by the process: " + string.Join(", ", lockingProcesses.Select(p => p.ProcessName).ToArray()); ds.LogError("\t" + message); entryErrors.TryAdd(entry, message); return(TagWriterReturnCode.IOEError); } // Otherwise if there is no locking process, this is a known issue in TagLib while using ReadStyle.PictureLazy. // Reattempt, using the default ReadStyle.Average else { ds.LogError("\t-> Reattempting load with different parameters..."); isRetry = true; goto Retry; } } } catch (Exception ex) { string message = "An error occurred while saving tags to \"" + entry.fileName + "\": (" + ex.Message + ")"; ds.LogError("\t" + message); entryErrors.TryAdd(entry, message); return(TagWriterReturnCode.OtherError); } file.Dispose(); return(TagWriterReturnCode.Success); }