aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Providers/MediaInfo/FFMpegAudioImageProvider.cs
blob: 421b0522da99e19e4a911b47e4a6bc12cd80625c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace MediaBrowser.Controller.Providers.MediaInfo
{
    /// <summary>
    /// Uses ffmpeg to create video images
    /// </summary>
    public class FFMpegAudioImageProvider : BaseFFMpegProvider<Audio>
    {
        public FFMpegAudioImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder)
            : base(logManager, configurationManager, mediaEncoder)
        {
        }

        /// <summary>
        /// The true task result
        /// </summary>
        protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);

        /// <summary>
        /// Gets the priority.
        /// </summary>
        /// <value>The priority.</value>
        public override MetadataProviderPriority Priority
        {
            get { return MetadataProviderPriority.Last; }
        }

        /// <summary>
        /// The _locks
        /// </summary>
        private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>();

        /// <summary>
        /// Gets the lock.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns>System.Object.</returns>
        private SemaphoreSlim GetLock(string filename)
        {
            return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
        }

        /// <summary>
        /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="force">if set to <c>true</c> [force].</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task{System.Boolean}.</returns>
        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
        {
            var success = ProviderRefreshStatus.Success;

            if (force || string.IsNullOrEmpty(item.PrimaryImagePath))
            {
                var album = item.ResolveArgs.Parent as MusicAlbum;

                if (album != null)
                {
                    // First try to use the parent's image
                    item.PrimaryImagePath = item.ResolveArgs.Parent.PrimaryImagePath;
                }

                // If it's still empty see if there's an embedded image
                if (force || string.IsNullOrEmpty(item.PrimaryImagePath))
                {
                    var audio = (Audio)item;

                    if (audio.MediaStreams != null && audio.MediaStreams.Any(s => s.Type == MediaStreamType.Video))
                    {
                        var filename = album != null && string.IsNullOrEmpty(audio.Album + album.DateModified.Ticks) ? (audio.Id.ToString() + audio.DateModified.Ticks) : audio.Album;

                        var path = Kernel.Instance.FFMpegManager.AudioImageCache.GetResourcePath(filename + "_primary", ".jpg");

                        if (!Kernel.Instance.FFMpegManager.AudioImageCache.ContainsFilePath(path))
                        {
                            var semaphore = GetLock(path);

                            // Acquire a lock
                            await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

                            // Check again
                            if (!Kernel.Instance.FFMpegManager.AudioImageCache.ContainsFilePath(path))
                            {
                                try
                                {
                                    await MediaEncoder.ExtractImage(new[] { audio.Path }, InputType.AudioFile, null, path, cancellationToken).ConfigureAwait(false);
                                }
                                catch
                                {
                                    success = ProviderRefreshStatus.Failure;
                                }
                                finally
                                {
                                    semaphore.Release();
                                }
                            }
                            else
                            {
                                semaphore.Release();
                            }
                        }

                        if (success == ProviderRefreshStatus.Success)
                        {
                            // Image is already in the cache
                            audio.PrimaryImagePath = path;
                        }
                    }
                }
            }

            SetLastRefreshed(item, DateTime.UtcNow, success);
            return true;
        }
    }
}