private void Button5_Click(object sender, EventArgs e) { var tf = TagLib.File.Create(@"e:\tmp\dadata.mp3"); var id3 = tf.GetTag(TagTypes.Id3v2) as TagLib.Id3v2.Tag; ReadOnlyByteVector USLT = "USLT"; //UnsynchronisedLyricsFrame uslt; //foreach (var frame in id3.GetFrames(USLT)) //{ // uslt = frame as UnsynchronisedLyricsFrame; //} //tf.Tag.Lyrics = "ascii?"; //List<Frame> frames = new List<Frame>(id3.GetFrames(USLT)); id3.RemoveFrames(USLT); UnsynchronisedLyricsFrame frame; frame = UnsynchronisedLyricsFrame.Get(id3, string.Empty, "eng", true); frame.Text = "今度こその新しい歌詞"; tf.Save(); }
/// <summary> /// Creates a deep copy of the current instance. /// </summary> /// <returns> /// A new <see cref="Frame" /> object identical to the /// current instance. /// </returns> public override Frame Clone() { var frame = new UnsynchronisedLyricsFrame(description, language, TextEncoding) { text = text }; return(frame); }
/// <summary> /// Gets a specified comments frame from the specified tag, /// trying to to match the description and language but /// accepting an incomplete match. /// </summary> /// <param name="tag"> /// A <see cref="Tag" /> object to search in. /// </param> /// <param name="description"> /// A <see cref="string" /> specifying the description to /// match. /// </param> /// <param name="language"> /// A <see cref="string" /> specifying the ISO-639-2 language /// code to match. /// </param> /// <returns> /// A <see cref="UnsynchronisedLyricsFrame" /> object /// containing the matching frame, or <see langword="null" /> /// if a match wasn't found. /// </returns> /// <remarks> /// <para>The method tries matching with the following order /// of precidence:</para> /// <list type="number"> /// <item><term>The first frame with a matching /// description and language.</term></item> /// <item><term>The first frame with a matching /// language.</term></item> /// <item><term>The first frame with a matching /// description.</term></item> /// <item><term>The first frame.</term></item> /// </list> /// </remarks> public static UnsynchronisedLyricsFrame GetPreferred(Tag tag, string description, string language) { // This is weird, so bear with me. The best thing we can // have is something straightforward and in our own // language. If it has a description, then it is // probably used for something other than an actual // comment. If that doesn't work, we'd still rather have // something in our language than something in another. // After that all we have left are things in other // languages, so we'd rather have one with actual // content, so we try to get one with no description // first. int best_value = -1; UnsynchronisedLyricsFrame best_frame = null; foreach (Frame frame in tag.GetFrames(FrameType.USLT)) { if (!(frame is UnsynchronisedLyricsFrame uslt)) { continue; } bool same_name = uslt.Description == description; bool same_lang = uslt.Language == language; if (same_name && same_lang) { return(uslt); } int value = same_lang ? 2 : same_name ? 1 : 0; if (value <= best_value) { continue; } best_value = value; best_frame = uslt; } return(best_frame); }
/// <summary> /// Gets a specified lyrics frame from the specified tag, /// optionally creating it if it does not exist. /// </summary> /// <param name="tag"> /// A <see cref="Tag" /> object to search in. /// </param> /// <param name="description"> /// A <see cref="string" /> specifying the description to /// match. /// </param> /// <param name="language"> /// A <see cref="string" /> specifying the ISO-639-2 language /// code to match. /// </param> /// <param name="create"> /// A <see cref="bool" /> specifying whether or not to create /// and add a new frame to the tag if a match is not found. /// </param> /// <returns> /// A <see cref="UnsynchronisedLyricsFrame" /> object /// containing the matching frame, or <see langword="null" /> /// if a match wasn't found and <paramref name="create" /> is /// <see langword="false" />. /// </returns> public static UnsynchronisedLyricsFrame Get(Tag tag, string description, string language, bool create) { UnsynchronisedLyricsFrame uslt; foreach (Frame frame in tag.GetFrames(FrameType.USLT)) { uslt = frame as UnsynchronisedLyricsFrame; if (uslt == null) { continue; } if (uslt.Description != description) { continue; } if (language != null && language != uslt.Language) { continue; } return(uslt); } if (!create) { return(null); } uslt = new UnsynchronisedLyricsFrame(description, language); tag.AddFrame(uslt); return(uslt); }
/// <summary> /// Save the Modified file /// </summary> /// <param name="track"></param> /// <param name="errorMessage"></param> /// <returns></returns> public static bool SaveFile(TrackData track, ref string errorMessage) { errorMessage = ""; if (!track.Changed) { return(true); } if (track.Readonly && !Options.MainSettings.ChangeReadOnlyAttributte && (Options.ReadOnlyFileHandling == 0 || Options.ReadOnlyFileHandling == 2)) { Form dlg = new ReadOnlyDialog(track.FullFileName); DialogResult dlgResult = dlg.ShowDialog(); switch (dlgResult) { case DialogResult.Yes: Options.ReadOnlyFileHandling = 0; // Yes break; case DialogResult.OK: Options.ReadOnlyFileHandling = 1; // Yes to All break; case DialogResult.No: Options.ReadOnlyFileHandling = 2; // No break; case DialogResult.Cancel: Options.ReadOnlyFileHandling = 3; // No to All break; } } if (track.Readonly) { if (!Options.MainSettings.ChangeReadOnlyAttributte && Options.ReadOnlyFileHandling > 1) { errorMessage = "File is readonly"; return(false); } try { System.IO.File.SetAttributes(track.FullFileName, System.IO.File.GetAttributes(track.FullFileName) & ~FileAttributes.ReadOnly); track.Readonly = false; } catch (Exception ex) { log.Error("File Save: Can't reset Readonly attribute: {0} {1}", track.FullFileName, ex.Message); errorMessage = ServiceScope.Get <ILocalisation>().ToString("message", "ErrorResetAttr"); return(false); } } TagLib.File file = null; bool error = false; try { TagLib.ByteVector.UseBrokenLatin1Behavior = true; file = TagLib.File.Create(track.FullFileName); } catch (CorruptFileException) { log.Warn("File Read: Ignoring track {0} - Corrupt File!", track.FullFileName); errorMessage = ServiceScope.Get <ILocalisation>().ToString("message", "CorruptFile"); error = true; } catch (UnsupportedFormatException) { log.Warn("File Read: Ignoring track {0} - Unsupported format!", track.FullFileName); errorMessage = ServiceScope.Get <ILocalisation>().ToString("message", "UnsupportedFormat"); error = true; } catch (FileNotFoundException) { log.Warn("File Read: Ignoring track {0} - Physical file no longer existing!", track.FullFileName); errorMessage = ServiceScope.Get <ILocalisation>().ToString("message", "NonExistingFile"); error = true; } catch (Exception ex) { log.Error("File Read: Error processing file: {0} {1}", track.FullFileName, ex.Message); errorMessage = string.Format(ServiceScope.Get <ILocalisation>().ToString("message", "ErrorReadingFile"), ex.Message); error = true; } if (file == null || error) { log.Error("File Read: Error processing file.: {0}", track.FullFileName); return(false); } try { // Get the ID3 Frame for ID3 specifc frame handling TagLib.Id3v1.Tag id3v1tag = null; TagLib.Id3v2.Tag id3v2tag = null; if (track.IsMp3) { id3v1tag = file.GetTag(TagTypes.Id3v1, true) as TagLib.Id3v1.Tag; id3v2tag = file.GetTag(TagTypes.Id3v2, true) as TagLib.Id3v2.Tag; } // Remove Tags, if they have been removed in TagEdit Panel foreach (TagLib.TagTypes tagType in track.TagsRemoved) { file.RemoveTags(tagType); } #region Main Tags string[] splitValues = track.Artist.Split(new[] { ';', '|' }); file.Tag.Performers = splitValues; splitValues = track.AlbumArtist.Split(new[] { ';', '|' }); file.Tag.AlbumArtists = splitValues; file.Tag.Album = track.Album.Trim(); file.Tag.BeatsPerMinute = (uint)track.BPM; if (track.Comment != "") { file.Tag.Comment = ""; if (track.IsMp3) { id3v1tag.Comment = track.Comment; foreach (Comment comment in track.ID3Comments) { CommentsFrame commentsframe = CommentsFrame.Get(id3v2tag, comment.Description, comment.Language, true); commentsframe.Text = comment.Text; commentsframe.Description = comment.Description; commentsframe.Language = comment.Language; } } else { file.Tag.Comment = track.Comment; } } else { file.Tag.Comment = ""; } if (track.IsMp3) { id3v2tag.IsCompilation = track.Compilation; } file.Tag.Disc = track.DiscNumber; file.Tag.DiscCount = track.DiscCount; splitValues = track.Genre.Split(new[] { ';', '|' }); file.Tag.Genres = splitValues; file.Tag.Title = track.Title; file.Tag.Track = track.TrackNumber; file.Tag.TrackCount = track.TrackCount; file.Tag.Year = (uint)track.Year; file.Tag.ReplayGainTrack = track.ReplayGainTrack; file.Tag.ReplayGainTrackPeak = track.ReplayGainTrackPeak; file.Tag.ReplayGainAlbum = track.ReplayGainAlbum; file.Tag.ReplayGainAlbumPeak = track.ReplayGainAlbumPeak; #endregion #region Detailed Information splitValues = track.Composer.Split(new[] { ';', '|' }); file.Tag.Composers = splitValues; file.Tag.Conductor = track.Conductor; file.Tag.Copyright = track.Copyright; file.Tag.Grouping = track.Grouping; splitValues = track.ArtistSortName.Split(new[] { ';', '|' }); file.Tag.PerformersSort = splitValues; splitValues = track.AlbumArtistSortName.Split(new[] { ';', '|' }); file.Tag.AlbumArtistsSort = splitValues; file.Tag.AlbumSort = track.AlbumSortName; file.Tag.TitleSort = track.TitleSortName; #endregion #region Picture List <TagLib.Picture> pics = new List <TagLib.Picture>(); foreach (Picture pic in track.Pictures) { TagLib.Picture tagPic = new TagLib.Picture(); try { byte[] byteArray = pic.Data; ByteVector data = new ByteVector(byteArray); tagPic.Data = data; tagPic.Description = pic.Description; tagPic.MimeType = "image/jpg"; tagPic.Type = pic.Type; pics.Add(tagPic); } catch (Exception ex) { log.Error("Error saving Picture: {0}", ex.Message); } file.Tag.Pictures = pics.ToArray(); } // Clear the picture if (track.Pictures.Count == 0) { file.Tag.Pictures = pics.ToArray(); } #endregion #region Lyrics if (track.Lyrics != null && track.Lyrics != "") { file.Tag.Lyrics = track.Lyrics; if (track.IsMp3) { foreach (Lyric lyric in track.LyricsFrames) { UnsynchronisedLyricsFrame lyricframe = UnsynchronisedLyricsFrame.Get(id3v2tag, lyric.Description, lyric.Language, true); lyricframe.Text = lyric.Text; lyricframe.Description = lyric.Description; lyricframe.Language = lyric.Language; } } else { file.Tag.Lyrics = track.Lyrics; } } else { file.Tag.Lyrics = ""; } #endregion #region Ratings if (track.IsMp3) { if (track.Ratings.Count > 0) { foreach (PopmFrame rating in track.Ratings) { PopularimeterFrame popmFrame = PopularimeterFrame.Get(id3v2tag, rating.User, true); popmFrame.Rating = Convert.ToByte(rating.Rating); popmFrame.PlayCount = Convert.ToUInt32(rating.PlayCount); } } else { id3v2tag.RemoveFrames("POPM"); } } else if (track.TagType == "ogg" || track.TagType == "flac") { if (track.Ratings.Count > 0) { XiphComment xiph = file.GetTag(TagLib.TagTypes.Xiph, true) as XiphComment; xiph.SetField("RATING", track.Rating.ToString()); } } #endregion #region Non- Standard Taglib and User Defined Frames if (Options.MainSettings.ClearUserFrames) { foreach (Frame frame in track.UserFrames) { ByteVector frameId = new ByteVector(frame.Id); if (frame.Id == "TXXX") { id3v2tag.SetUserTextAsString(frame.Description, ""); } else { id3v2tag.SetTextFrame(frameId, ""); } } } List <Frame> allFrames = new List <Frame>(); allFrames.AddRange(track.Frames); // The only way to avoid duplicates of User Frames is to delete them by assigning blank values to them if (track.SavedUserFrames != null && !Options.MainSettings.ClearUserFrames) { // Clean the previously saved Userframes, to avoid duplicates foreach (Frame frame in track.SavedUserFrames) { ByteVector frameId = new ByteVector(frame.Id); if (frame.Id == "TXXX") { id3v2tag.SetUserTextAsString(frame.Description, ""); } else { id3v2tag.SetTextFrame(frameId, ""); } } allFrames.AddRange(track.UserFrames); } foreach (Frame frame in allFrames) { ByteVector frameId = new ByteVector(frame.Id); // The only way to avoid duplicates of User Frames is to delete them by assigning blank values to them if (frame.Id == "TXXX") { if (frame.Description != "") { id3v2tag.SetUserTextAsString(frame.Description, ""); id3v2tag.SetUserTextAsString(frame.Description, frame.Value); } } else { id3v2tag.SetTextFrame(frameId, ""); id3v2tag.SetTextFrame(frameId, frame.Value); } } #endregion // Now, depending on which frames the user wants to save, we will remove the other Frames file = Util.FormatID3Tag(file); // Set the encoding for ID3 Tags if (track.IsMp3) { TagLib.Id3v2.Tag.ForceDefaultEncoding = true; switch (Options.MainSettings.CharacterEncoding) { case 0: TagLib.Id3v2.Tag.DefaultEncoding = StringType.Latin1; break; case 1: TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF16; break; case 2: TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF16BE; break; case 3: TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF8; break; case 4: TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF16LE; break; } } // Save the file file.Save(); } catch (Exception ex) { log.Error("File Save: Error processing file: {0} {1}", track.FullFileName, ex.Message); errorMessage = ServiceScope.Get <ILocalisation>().ToString("message", "ErrorSave"); error = true; } if (error) { return(false); } return(true); }
/// <summary> /// Save the Modified file /// </summary> /// <param name="song"></param> /// <param name="errorMessage"></param> /// <returns></returns> public static bool SaveFile(SongData song, ref string errorMessage) { errorMessage = ""; if (!song.Changed) { return(true); } var options = (ServiceLocator.Current.GetInstance(typeof(ISettingsManager)) as ISettingsManager)?.GetOptions; /* * if (song.Readonly && !options.MainSettings.ChangeReadOnlyAttributte && * (options.ReadOnlyFileHandling == 0 || options.ReadOnlyFileHandling == 2)) * { * Form dlg = new ReadOnlyDialog(song.FullFileName); * DialogResult dlgResult = dlg.ShowDialog(); * * switch (dlgResult) * { * case DialogResult.Yes: * options.ReadOnlyFileHandling = 0; // Yes * break; * * case DialogResult.OK: * options.ReadOnlyFileHandling = 1; // Yes to All * break; * * case DialogResult.No: * options.ReadOnlyFileHandling = 2; // No * break; * * case DialogResult.Cancel: * options.ReadOnlyFileHandling = 3; // No to All * break; * } * } */ if (song.Readonly) { if (!options.MainSettings.ChangeReadOnlyAttribute && options.ReadOnlyFileHandling > 1) { errorMessage = "File is readonly"; return(false); } try { System.IO.File.SetAttributes(song.FullFileName, System.IO.File.GetAttributes(song.FullFileName) & ~FileAttributes.ReadOnly); song.Readonly = false; } catch (Exception ex) { log.Error($"File Save: Can't reset Readonly attribute: {song.FullFileName} {ex.Message}"); errorMessage = LocalizeDictionary.Instance.GetLocalizedObject("MPTagThat", "Strings", "message_ErrorResetAttr", LocalizeDictionary.Instance.Culture).ToString(); return(false); } } TagLib.File file = null; bool error = false; try { TagLib.ByteVector.UseBrokenLatin1Behavior = true; file = TagLib.File.Create(song.FullFileName); } catch (CorruptFileException) { log.Warn($"File Read: Ignoring song {song.FullFileName} - Corrupt File!"); errorMessage = LocalizeDictionary.Instance.GetLocalizedObject("MPTagThat", "Strings", "message_CorruptFile", LocalizeDictionary.Instance.Culture).ToString(); error = true; } catch (UnsupportedFormatException) { log.Warn($"File Read: Ignoring song {song.FullFileName} - Unsupported format!"); errorMessage = LocalizeDictionary.Instance.GetLocalizedObject("MPTagThat", "Strings", "message_UnsupportedFormat", LocalizeDictionary.Instance.Culture).ToString(); error = true; } catch (FileNotFoundException) { log.Warn($"File Read: Ignoring song {song.FullFileName} - Physical file no longer existing!"); errorMessage = LocalizeDictionary.Instance.GetLocalizedObject("MPTagThat", "Strings", "message_NonExistingFile", LocalizeDictionary.Instance.Culture).ToString(); error = true; } catch (Exception ex) { log.Error($"File Read: Error processing file: {song.FullFileName} {ex.Message}"); errorMessage = string.Format(LocalizeDictionary.Instance.GetLocalizedObject("MPTagThat", "Strings", "message_ErrorReadingFile", LocalizeDictionary.Instance.Culture).ToString(), song.FullFileName); error = true; } if (file == null || error) { log.Error("File Read: Error processing file.: {0}", song.FullFileName); return(false); } try { // Get the ID3 Frame for ID3 specifc frame handling TagLib.Id3v1.Tag id3v1tag = null; TagLib.Id3v2.Tag id3v2tag = null; if (song.IsMp3) { id3v1tag = file.GetTag(TagTypes.Id3v1, true) as TagLib.Id3v1.Tag; id3v2tag = file.GetTag(TagTypes.Id3v2, true) as TagLib.Id3v2.Tag; } // Remove Tags, if they have been removed in TagEdit Panel foreach (TagLib.TagTypes tagType in song.TagsRemoved) { file.RemoveTags(tagType); } if (file.Tag != null) { #region Main Tags string[] splitValues = song.Artist.Split(new[] { ';', '|' }); file.Tag.Performers = splitValues; splitValues = song.AlbumArtist.Split(new[] { ';', '|' }); file.Tag.AlbumArtists = splitValues; file.Tag.Album = song.Album.Trim(); file.Tag.BeatsPerMinute = (uint)song.BPM; if (song.Comment != "") { file.Tag.Comment = ""; if (song.IsMp3) { id3v1tag.Comment = song.Comment; foreach (Comment comment in song.ID3Comments) { CommentsFrame commentsframe = CommentsFrame.Get(id3v2tag, comment.Description, comment.Language, true); commentsframe.Text = comment.Text; commentsframe.Description = comment.Description; commentsframe.Language = comment.Language; } } else { file.Tag.Comment = song.Comment; } } else { if (song.IsMp3 && id3v2tag != null) { id3v2tag.RemoveFrames("COMM"); } else { file.Tag.Comment = ""; } } if (song.IsMp3) { id3v2tag.IsCompilation = song.Compilation; } file.Tag.Disc = song.DiscNumber; file.Tag.DiscCount = song.DiscCount; splitValues = song.Genre.Split(new[] { ';', '|' }); file.Tag.Genres = splitValues; file.Tag.Title = song.Title; file.Tag.Track = song.TrackNumber; file.Tag.TrackCount = song.TrackCount; file.Tag.Year = (uint)song.Year; double gain; var replayGainTrack = string.IsNullOrEmpty(song.ReplayGainTrack) ? "" : song.ReplayGainTrack.Substring(0, song.ReplayGainTrack.IndexOf(" ", StringComparison.Ordinal)); if (double.TryParse(replayGainTrack, NumberStyles.Any, CultureInfo.InvariantCulture, out gain)) { file.Tag.ReplayGainTrackGain = gain; } if (Double.TryParse(song.ReplayGainTrackPeak, NumberStyles.Any, CultureInfo.InvariantCulture, out gain)) { file.Tag.ReplayGainTrackPeak = gain; } var replayGainAlbum = string.IsNullOrEmpty(song.ReplayGainAlbum) ? "" : song.ReplayGainAlbum.Substring(0, song.ReplayGainAlbum.IndexOf(" ", StringComparison.Ordinal)); if (Double.TryParse(replayGainAlbum, NumberStyles.Any, CultureInfo.InvariantCulture, out gain)) { file.Tag.ReplayGainAlbumGain = gain; } if (Double.TryParse(song.ReplayGainAlbumPeak, NumberStyles.Any, CultureInfo.InvariantCulture, out gain)) { file.Tag.ReplayGainAlbumPeak = gain; } #endregion #region MusicBrainz file.Tag.MusicBrainzArtistId = song.MusicBrainzArtistId; file.Tag.MusicBrainzDiscId = song.MusicBrainzDiscId; file.Tag.MusicBrainzReleaseArtistId = song.MusicBrainzReleaseArtistId; file.Tag.MusicBrainzReleaseCountry = song.MusicBrainzReleaseCountry; file.Tag.MusicBrainzReleaseGroupId = song.MusicBrainzReleaseGroupId; file.Tag.MusicBrainzReleaseId = song.MusicBrainzReleaseId; file.Tag.MusicBrainzReleaseStatus = song.MusicBrainzReleaseStatus; file.Tag.MusicBrainzTrackId = song.MusicBrainzTrackId; file.Tag.MusicBrainzReleaseType = song.MusicBrainzReleaseType; #endregion #region Detailed Information splitValues = song.Composer.Split(new[] { ';', '|' }); file.Tag.Composers = splitValues; file.Tag.Conductor = song.Conductor; file.Tag.Copyright = song.Copyright; file.Tag.Grouping = song.Grouping; splitValues = song.ArtistSortName.Split(new[] { ';', '|' }); file.Tag.PerformersSort = splitValues; splitValues = song.AlbumArtistSortName.Split(new[] { ';', '|' }); file.Tag.AlbumArtistsSort = splitValues; file.Tag.AlbumSort = song.AlbumSortName; file.Tag.TitleSort = song.TitleSortName; #endregion #region Picture List <TagLib.Picture> pics = new List <TagLib.Picture>(); foreach (Picture pic in song.Pictures) { TagLib.Picture tagPic = new TagLib.Picture(); try { byte[] byteArray = pic.Data; ByteVector data = new ByteVector(byteArray); tagPic.Data = data; tagPic.Description = pic.Description; tagPic.MimeType = "image/jpg"; tagPic.Type = pic.Type; pics.Add(tagPic); } catch (Exception ex) { log.Error("Error saving Picture: {0}", ex.Message); } file.Tag.Pictures = pics.ToArray(); } // Clear the picture if (song.Pictures.Count == 0) { file.Tag.Pictures = pics.ToArray(); } #endregion #region Lyrics if (song.Lyrics != null && song.Lyrics != "") { file.Tag.Lyrics = song.Lyrics; if (song.IsMp3) { id3v2tag.RemoveFrames("USLT"); foreach (Lyric lyric in song.LyricsFrames) { UnsynchronisedLyricsFrame lyricframe = UnsynchronisedLyricsFrame.Get(id3v2tag, lyric.Description, lyric.Language, true); lyricframe.Text = lyric.Text; lyricframe.Description = lyric.Description; lyricframe.Language = lyric.Language; } } else { file.Tag.Lyrics = song.Lyrics; } } else { file.Tag.Lyrics = ""; } #endregion #region Ratings if (song.IsMp3) { id3v2tag.RemoveFrames("POPM"); if (song.Ratings.Count > 0) { foreach (PopmFrame rating in song.Ratings) { PopularimeterFrame popmFrame = PopularimeterFrame.Get(id3v2tag, rating.User, true); popmFrame.Rating = Convert.ToByte(rating.Rating); popmFrame.PlayCount = Convert.ToUInt32(rating.PlayCount); } } } else if (song.TagType == "ogg" || song.TagType == "flac") { if (song.Ratings.Count > 0) { XiphComment xiph = file.GetTag(TagLib.TagTypes.Xiph, true) as XiphComment; xiph.SetField("RATING", song.Rating.ToString()); } } #endregion #region Non- Standard Taglib and User Defined Frames if (options.MainSettings.ClearUserFrames) { foreach (Frame frame in song.UserFrames) { ByteVector frameId = new ByteVector(frame.Id); if (frame.Id == "TXXX") { id3v2tag.SetUserTextAsString(frame.Description, "", true); } else { id3v2tag.SetTextFrame(frameId, ""); } } } List <Frame> allFrames = new List <Frame>(); allFrames.AddRange(song.Frames); // The only way to avoid duplicates of User Frames is to delete them by assigning blank values to them if (song.SavedUserFrames != null && !options.MainSettings.ClearUserFrames) { // Clean the previously saved Userframes, to avoid duplicates foreach (Frame frame in song.SavedUserFrames) { ByteVector frameId = new ByteVector(frame.Id); if (frame.Id == "TXXX") { id3v2tag.SetUserTextAsString(frame.Description, "", true); } else { id3v2tag.SetTextFrame(frameId, ""); } } allFrames.AddRange(song.UserFrames); } foreach (Frame frame in allFrames) { ByteVector frameId = new ByteVector(frame.Id); // The only way to avoid duplicates of User Frames is to delete them by assigning blank values to them if (frame.Id == "TXXX") { if (frame.Description != "") { id3v2tag.SetUserTextAsString(frame.Description, "", true); id3v2tag.SetUserTextAsString(frame.Description, frame.Value, true); } } else { id3v2tag.SetTextFrame(frameId, ""); id3v2tag.SetTextFrame(frameId, frame.Value); } } #endregion // Now, depending on which frames the user wants to save, we will remove the other Frames file = Util.FormatID3Tag(file); // Set the encoding for ID3 Tags if (song.IsMp3) { TagLib.Id3v2.Tag.ForceDefaultEncoding = true; switch (options.MainSettings.CharacterEncoding) { case "Latin1": TagLib.Id3v2.Tag.DefaultEncoding = StringType.Latin1; break; case "UTF16": TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF16; break; case "UTF16-BE": TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF16BE; break; case "UTF8": TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF8; break; case "UTF16-LE": TagLib.Id3v2.Tag.DefaultEncoding = StringType.UTF16LE; break; } } } // Save the file file.Save(); } catch (Exception ex) { log.Error("File Save: Error processing file: {0} {1}", song.FullFileName, ex.Message); errorMessage = string.Format(LocalizeDictionary.Instance.GetLocalizedObject("MPTagThat", "Strings", "message_ErrorSave", LocalizeDictionary.Instance.Culture).ToString(), song.FullFileName); error = true; } if (error) { return(false); } return(true); }