QuickImage – Rev 2

Subversion Repositories:
Rev:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using ImageMagick;
using ImageMagick.Formats;
using QuickImage.Utilities;
using Serilog;

namespace QuickImage
{
    public class TagManager
    {
        private readonly MagickReadSettings _magickReaderSettings;
        private readonly FileMutex _fileMutex;

        private TagManager()
        {
            _magickReaderSettings = new MagickReadSettings { FrameIndex = 0, FrameCount = 1 };
        }

        public TagManager(FileMutex fileMutex) : this()
        {
            _fileMutex = fileMutex;
        }

        public async Task<bool> StripIptcProfile(string fileName, CancellationToken cancellationToken)
        {
            await _fileMutex[fileName].WaitAsync(cancellationToken);

            try
            {
                using var imageCollection = new MagickImageCollection(fileName);
                using var image = imageCollection[0];

                var profile = image.GetIptcProfile();

                if (profile == null)
                {
                    return true;
                }

                image.RemoveProfile(profile);
                await imageCollection.WriteAsync(fileName, cancellationToken);

                return true;
            }
            catch (Exception exception)
            {
                Log.Error(exception, "Could not add keywords.");
            }
            finally
            {
                _fileMutex[fileName].Release();
            }

            return false;
        }

        public async Task<bool> AddIptcKeywords(string fileName, IEnumerable<string> keywords,
            CancellationToken cancellationToken)
        {
            await _fileMutex[fileName].WaitAsync(cancellationToken);

            try
            {
                using var imageCollection = new MagickImageCollection(fileName);
                using var image = imageCollection[0];
                
                var profile = image.GetIptcProfile();

                if (profile == null)
                {
                    profile = new IptcProfile();

                    foreach (var keyword in keywords)
                    {
                        profile.SetValue(IptcTag.Keyword, keyword);
                    }

                    image.SetProfile(profile);

                    await imageCollection.WriteAsync(fileName, cancellationToken);

                    return true;
                }

                var union = keywords.Union(profile.GetAllValues(IptcTag.Keyword).Select(iptcValue => iptcValue.Value),
                    StringComparer.Ordinal).ToList();
                profile.RemoveValue(IptcTag.Keyword);
                foreach (var keyword in union)
                {
                    profile.SetValue(IptcTag.Keyword, keyword);
                }

                image.SetProfile(profile);
                using var writeStream = new FileStream(fileName, FileMode.OpenOrCreate);
                await imageCollection.WriteAsync(writeStream, cancellationToken);

                return true;
            }
            catch (Exception exception)
            {
                Log.Error(exception, "Could not add keywords.");
            }
            finally
            {
                _fileMutex[fileName].Release();
            }

            return false;
        }

        public async Task<bool> RemoveIptcKeywords(string fileName, IEnumerable<string> keywords, CancellationToken cancellationToken)
        {
            await _fileMutex[fileName].WaitAsync(cancellationToken);

            try
            {
                using var imageCollection = new MagickImageCollection(fileName);
                using var image = imageCollection[0];

                var profile = image.GetIptcProfile();

                if (profile == null)
                {
                    return true;
                }

                foreach (var keyword in keywords)
                {
                    profile.RemoveValue(IptcTag.Keyword, keyword);
                }

                image.SetProfile(profile);
                imageCollection.Optimize();
                await imageCollection.WriteAsync(fileName, cancellationToken);

                return true;
            }
            catch (Exception exception)
            {
                Log.Error(exception, "Could not remove keywords.");
            }
            finally
            {
                _fileMutex[fileName].Release();
            }

            return false;
        }

        public async IAsyncEnumerable<string> GetIptcKeywords(string fileName, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            await _fileMutex[fileName].WaitAsync(cancellationToken);

            try
            {
                using var imageCollection = new MagickImageCollection(fileName, _magickReaderSettings);
                using var image = imageCollection[0];

                var profile = image.GetIptcProfile();

                if (profile == null)
                {
                    yield break;
                }

                foreach (var keyword in profile.GetAllValues(IptcTag.Keyword))
                {
                    yield return keyword.Value;
                }
            }
            finally
            {
                _fileMutex[fileName].Release();
            }
        }

        public IEnumerable<string> GetIptcKeywords(IMagickImage<ushort> imageFrame)
        {
            var profile = imageFrame.GetIptcProfile();

            if (profile == null)
            {
                yield break;
            }

            foreach (var keyword in profile.GetAllValues(IptcTag.Keyword))
            {
                yield return keyword.Value;
            }

        }
    }
}