Ejemplo n.º 1
0
        public AudioMetadata ReadMetadata(Stream stream)
        {
            TagModel tagModel;

            try
            {
                tagModel = TagManager.Deserialize(stream);
            }
            catch (TagNotFoundException)
            {
                try
                {
                    // If no ID3v2 tag was found, check for ID3v1
                    var v1Tag = new ID3v1();
                    v1Tag.Deserialize(stream);
                    tagModel = v1Tag.FrameModel;
                }
                catch (TagNotFoundException e)
                {
                    throw new AudioUnsupportedException(e.Message);
                }
            }

            return(new TagModelToMetadataAdapter(tagModel));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Parse ID3 tags from stream and calculate where the audio must be
        /// </summary>
        /// <remarks>
        /// if this throws an exception, the File is not usable.
        /// </remarks>
        private void Initialise()
        {
            // create an empty framemodel, to use if we don't parse anything better
            FrameModel tagModel = new FrameModel();

            // clear out any previous taghandler
            _tagHandler = null;

            // don't know how big the audio is until we've parsed the tags
            _audio = null;
            UInt32 audioNumBytes;

            using (FileStream sourceStream = _sourceFileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                // all the header calculations use UInt32;
                // this guarantees all the file offsets we have to deal with fit in a UInt32
                if (sourceStream.Length > UInt32.MaxValue)
                {
                    throw new InvalidAudioFrameException("MP3 file can't be bigger than 4gb");
                }

                // in the absence of any recognised tags,
                // audio starts at the start
                _audioStart = 0;
                // audio is entire file length
                audioNumBytes = (UInt32)sourceStream.Length;

                // try to read an ID3v1 block.
                // If ID3v2 block exists, its values overwrite these
                // Otherwise, if ID3V1 block exists, its values are used
                // The audio is anything that's left after all the tags are excluded.
                try
                {
                    ID3v1 id3v1 = new ID3v1();
                    id3v1.Deserialize(sourceStream);

                    // fill in ID3v2 block from the ID3v1 data
                    tagModel = id3v1.FrameModel;

                    // audio is shorter by the length of the id3v1 tag
                    audioNumBytes -= ID3v1.TagLength;
                }
                catch (TagNotFoundException)
                {
                    // ignore "no ID3v1 block"
                    // everything else isn't caught here, and throws out to the caller
                }

                try
                {
                    sourceStream.Seek(0, SeekOrigin.Begin);
                    tagModel = FrameManager.Deserialize(sourceStream);

                    // audio starts after the tag
                    _audioStart = (uint)sourceStream.Position;
                    // audio is shorter by the length of the id3v2 tag
                    audioNumBytes -= _audioStart;
                }
                catch (TagNotFoundException)
                {
                    // ignore "no ID3v2 block"
                    // everything else isn't caught here, and throws out to the caller
                }

                // create a taghandler to hold the tagmodel we've parsed, if any
                _tagHandler = new TagHandler(tagModel);
            } // closes sourceStream

            // save the location of the audio in the original file
            // passing in audio size and id3 length tag (if any) to help with bitrate calculations
            _audio         = new AudioFile(_sourceFileInfo, _audioStart, audioNumBytes, _tagHandler.Length);
            _audioReplaced = false;
        }
Ejemplo n.º 3
0
        public async Task RetagFileAsync(string mp3FilePath)
        {
            VerboseOutput?.Invoke(this, new VerboseInfo(0, () => $"File: \"{mp3FilePath}\""));

            using (var stream = new FileStream(mp3FilePath, FileMode.Open, _options.DryRunMode ? FileAccess.Read : FileAccess.ReadWrite))
            {
                TagHandler tag1      = null;
                TagHandler tag2      = null;
                ID3v1      tagModel1 = null;

                try
                {
                    // Read ID3v1 tags...
                    stream.Seek(0, SeekOrigin.Begin);
                    tagModel1 = new ID3v1();
                    tagModel1.Deserialize(stream);

                    tag1 = new TagHandler(tagModel1.FrameModel);
                }
                catch (Exception ex)
                {
                    VerboseOutput?.Invoke(this, new VerboseInfo(3, () => $"    Could not read ID3v1 tags: {ex.ToString()}", isError: true));
                }

                try
                {
                    // Read ID3v2 tags...
                    stream.Seek(0, SeekOrigin.Begin);
                    var tagModel2 = TagManager.Deserialize(stream);

                    tag2 = new TagHandler(tagModel2);
                }
                catch (Exception ex)
                {
                    VerboseOutput?.Invoke(this, new VerboseInfo(3, () => $"    Could not read ID3v2 tags: {ex.ToString()}", isError: true));
                }

                if (tag1 == null && tag2 == null)
                {
                    VerboseOutput?.Invoke(this, new VerboseInfo(0, () => $"    No ID3 tags found. No changes.", isError: true));
                    return;
                }

                if (string.IsNullOrWhiteSpace(tag1?.Year) && tag2 != null)
                {
                    tag1 = null;                     // prefer storing the year in v2 by checking v1 first
                }
                if (string.IsNullOrWhiteSpace(tag2?.Year) && tag1 != null)
                {
                    tag2 = null;                     // The year is stored in the v1 tag only
                }
                string artist = (tag2?.Artist ?? tag1?.Artist).Trim(new char[] { ' ', '\n', '\0' }).Trim();
                string title  = (tag2?.Title ?? tag1?.Title).Trim(new char[] { ' ', '\n', '\0' }).Trim();
                string year   = (tag2?.Year ?? tag1?.Year).Trim(new char[] { ' ', '\n', '\0' }).Trim();

                if (tag1 != null && tag2 != null && tag1.Year != tag2.Year)
                {
                    year = $"{tag1.Year} & {tag2.Year}";
                }

                if (string.IsNullOrWhiteSpace(artist) || string.IsNullOrWhiteSpace(title))
                {
                    VerboseOutput?.Invoke(this, new VerboseInfo(0, () => $"    Title and/or artist tags are missing or contain empty values. No changes.", isError: true));
                    return;
                }

                VerboseOutput?.Invoke(this, new VerboseInfo(1, () => $"    Found MP3 Tags: \"{artist}\" \"{title}\" ({year})"));
                var oldestReleaseDate = await GetOldestReleaseDateAsync(artist, title);

                if (oldestReleaseDate == null)
                {
                    VerboseOutput?.Invoke(this, new VerboseInfo(0, () => $"    Oldest release date could not be determined. Online lookup failed. No changes.", isError: true));
                    return;
                }

                if (year == oldestReleaseDate.Value.Year.ToString())
                {
                    VerboseOutput?.Invoke(this, new VerboseInfo(0, () => $"    Year is already correct. No changes."));
                    return;
                }

                if (_options.DryRunMode)
                {
                    VerboseOutput?.Invoke(this, new VerboseInfo(0, () => $"    DRY RUN MODE: Year WOULD HAVE BEEN updated to ({oldestReleaseDate.Value.Year})."));
                }
                else
                {
                    // Update the year with the new looked up year
                    if (tag1 != null)
                    {
                        tag1.Year = oldestReleaseDate.Value.Year.ToString();

                        tagModel1.FrameModel = tag1.FrameModel;
                        tagModel1.Serialize(stream);
                    }

                    if (tag2 != null)
                    {
                        tag2.Year = oldestReleaseDate.Value.Year.ToString();

                        TagManager.Serialize(tag2.FrameModel, stream);
                    }

                    VerboseOutput?.Invoke(this, new VerboseInfo(0, () => $"    Year updated to ({oldestReleaseDate.Value.Year})."));
                }
            }
        }