public static MusicFileTag ConvertTagToMusicFileTag(TagLib.Id3v1.Tag tagV1, TagLib.Id3v2.Tag tagV2, string filePath) { var tmp = new MusicFileTag { // File Details FilePath = filePath, FileName = Path.GetFileName(filePath), FileSize = Logic.BasicFunctions.FormatFileSize(new FileInfo(filePath).Length), }; // TagTypes TagTypes myTagTypes = TagLib.File.Create(filePath).TagTypesOnDisk; tmp.EnabledV1 = myTagTypes.ToString().ToLower().Contains("id3v1") ? true : false; tmp.EnabledV2 = myTagTypes.ToString().ToLower().Contains("id3v2") ? true : false; // V1 if (tmp.EnabledV1) { tmp.ArtistV1 = tagV1.FirstPerformer == null ? "" : tagV1.FirstPerformer.Replace("�", string.Empty); tmp.AlbumV1 = tagV1.Album == null ? "" : tagV1.Album.Replace("�", string.Empty); tmp.GenreV1 = tagV1.FirstGenre == null ? "" : tagV1.FirstGenre.Replace("�", string.Empty); tmp.YearV1 = tagV1.Year.ToString() == null ? "" : tagV1.Year.ToString().Replace("�", string.Empty); tmp.TitleV1 = tagV1.Title == null ? "" : tagV1.Title.Replace("�", string.Empty); tmp.CommentV1 = tagV1.Comment == null ? "" : tagV1.Comment.Replace("�", string.Empty); tmp.TrackV1 = tagV1.Track.ToString() == null ? "" : tagV1.Track.ToString().Replace("�", string.Empty); } // V2 if (tmp.EnabledV2) { tmp.VersionV2 = tagV2.Version.ToString(); // Version 3 tmp.PlayListDelayV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TDLY").Replace("�", string.Empty); tmp.TrackNumberV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TRCK").Replace("�", string.Empty); tmp.PartOfSetV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TPOS").Replace("�", string.Empty); tmp.BPMV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TBPM").Replace("�", string.Empty); tmp.ArtistV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TPE1").Replace("�", string.Empty); tmp.GenreV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TCON").Replace("�", string.Empty); tmp.LanguageV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TLAN").Replace("�", string.Empty); tmp.KeyV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TKEY").Replace("�", string.Empty); tmp.SetSubtitleV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TSST").Replace("�", string.Empty); tmp.ContentDescriptionV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TIT1").Replace("�", string.Empty); tmp.InterpretedV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TPE4").Replace("�", string.Empty); tmp.AlbumV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TALB").Replace("�", string.Empty); tmp.TitleV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TIT2").Replace("�", string.Empty); /// Frames from /// http://id3.org/id3v2.3.0#Declared_ID3v2_frames // + Version 4 if (tagV2.Version == 4) { tmp.TitleSortV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TSOT").Replace("�", string.Empty); tmp.AlbumSortV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TSOA").Replace("�", string.Empty); tmp.MoodV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TMOO").Replace("�", string.Empty); tmp.SubtitleV2 = Logic.TaggingLogic.GetTagContent(tagV2, "TIT3").Replace("�", string.Empty); } } else { tmp.VersionV2 = "4"; } return(tmp); }
/// <summary> /// /// </summary> /// <param name="input"></param> /// <param name="caller"></param> public virtual void Parse(Stream input, TagTypes caller) { BinaryReader br = new BinaryReader(input); this._width = br.ReadUInt16(); if (caller.Equals(TagTypes.DefineShape) || caller.Equals(TagTypes.DefineShape2)) { this._color = new Rgb(this._SwfVersion); try { this._color.Parse(input); } catch(SwfFormatException e) { throw e; } } else if (caller.Equals(TagTypes.DefineShape3)) { this._color = new Rgba(this._SwfVersion); try { this._color.Parse(input); } catch (SwfFormatException e) { throw e; } } else { SwfFormatException e = new SwfFormatException("LineStyle was called by illegal TagType (" + caller.ToString() +")."); Log.Error(this, e.Message); } }
/// <summary> /// /// </summary> /// <param name="input"></param> /// <param name="caller"></param> public void Parse(Stream input, TagTypes caller) { BinaryReader br = new BinaryReader(input); this._caller = caller; this._fillStyleType = (FillStyleType)br.ReadByte(); if (this._fillStyleType.Equals(FillStyleType.SolidFill)) { if (caller.Equals(TagTypes.DefineShape3) ) { this._color = new Rgba(this._SwfVersion); try { this._color.Parse(input); //Log.InfoFormat("Valid fill style type. FillstyleType: 0x{0:X2} at address: 0x{1:X2} in {2}", (Int32)this._fillStyleType, ((Int32)input.Position) - 1, caller); } catch (SwfFormatException e) { throw e; } } else if (caller.Equals(TagTypes.DefineShape4)) { this._color = new Rgba(this._SwfVersion); try { this._color.Parse(input); } catch (SwfFormatException e) { throw e; } } else { this._color = new Rgb(this._SwfVersion); try { this._color.Parse(input); } catch (SwfFormatException e) { throw e; } } } else if (this._fillStyleType.Equals(FillStyleType.LinearGradientFill) || this._fillStyleType.Equals(FillStyleType.RadialGradientFill)) { this._gradientMatrix = new Matrix(this._SwfVersion); try { this._gradientMatrix.Parse(input); } catch (SwfFormatException e) { throw e; } this._gradient = new Gradient(this._SwfVersion); try { this._gradient.Parse(input, caller); //Log.InfoFormat("Valid fill style type. FillstyleType: 0x{0:X2} at address: 0x{1:X4} in {2}", (Int32)this._fillStyleType, ((Int32)input.Position) - 1, caller); } catch (SwfFormatException e) { throw e; } } else if (this._fillStyleType.Equals(FillStyleType.FocalRadialGradientFill)) { if (this._SwfVersion >= 8) { this._gradientMatrix = new Matrix(this._SwfVersion); try { this._gradientMatrix.Parse(input); } catch(SwfFormatException e) { throw e; } this._gradient = new FocalGradient(this._SwfVersion); try { this._gradient.Parse(input, caller); //Log.InfoFormat("Valid fill style type. FillstyleType: 0x{0:X2} at address: 0x{1:X4} in {2}", (Int32)this._fillStyleType, ((Int32)input.Position) - 1, caller); } catch (SwfFormatException e) { throw e; } } else { SwfFormatException e = new SwfFormatException("Focal gradients are supported by Swf 8 and later only. This version is: " + this._SwfVersion.ToString()); Log.Error(this, e); } } else if (this._fillStyleType.Equals(FillStyleType.RepeatingBitmapFill) || this._fillStyleType.Equals(FillStyleType.ClippedBitmapFill) || this._fillStyleType.Equals(FillStyleType.NonSmoothedRepeatingBitmap) || this._fillStyleType.Equals(FillStyleType.NonSmoothedClippedBitmap)) { this._bitmapID = br.ReadUInt16(); this._bitmapMatrix = new Matrix(this._SwfVersion); try { this._bitmapMatrix.Parse(input); //Log.InfoFormat("Valid fill style type. FillstyleType: 0x{0:X2} at address: 0x{1:X4} in {2}", (Int32)this._fillStyleType, ((Int32)input.Position) - 1, caller); } catch (SwfFormatException e) { throw e; } } else { SwfFormatException e = new SwfFormatException("Invalid fill style type! (" + this._fillStyleType +")" +" caller: " + caller.ToString()); Log.Error(this, e); throw e; } }
// --- 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); }