using System.IO; using System.Threading; using System.Threading.Tasks; using System.Xml; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Books.OpenPackagingFormat { /// /// Provides metadata for book items that have an OPF file in the same directory. Supports the standard /// content.opf filename, bespoke metadata.opf name from Calibre libraries, and OPF files that have the /// same name as their respective books for directories with several books. /// public class OpfProvider : ILocalMetadataProvider, IHasItemChangeMonitor { private const string StandardOpfFile = "content.opf"; private const string CalibreOpfFile = "metadata.opf"; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. public OpfProvider(IFileSystem fileSystem, ILogger logger) { _fileSystem = fileSystem; _logger = logger; } /// public string Name => "Open Packaging Format"; /// public bool HasChanged(BaseItem item, IDirectoryService directoryService) { var file = GetXmlFile(item.Path); return file.Exists && _fileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved; } /// public Task> GetMetadata(ItemInfo info, IDirectoryService directoryService, CancellationToken cancellationToken) { var path = GetXmlFile(info.Path).FullName; try { return Task.FromResult(ReadOpfData(path, cancellationToken)); } catch (FileNotFoundException) { return Task.FromResult(new MetadataResult { HasMetadata = false }); } } private FileSystemMetadata GetXmlFile(string path) { var fileInfo = _fileSystem.GetFileSystemInfo(path); var directoryInfo = fileInfo.IsDirectory ? fileInfo : _fileSystem.GetDirectoryInfo(Path.GetDirectoryName(path)!); // check for OPF with matching name first since it's the most specific filename var specificFile = Path.Combine(directoryInfo.FullName, Path.GetFileNameWithoutExtension(path) + ".opf"); var file = _fileSystem.GetFileInfo(specificFile); if (file.Exists) { return file; } file = _fileSystem.GetFileInfo(Path.Combine(directoryInfo.FullName, StandardOpfFile)); // check metadata.opf last since it's really only used by Calibre return file.Exists ? file : _fileSystem.GetFileInfo(Path.Combine(directoryInfo.FullName, CalibreOpfFile)); } private MetadataResult ReadOpfData(string file, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var doc = new XmlDocument(); doc.Load(file); var utilities = new OpfReader(doc, _logger); return utilities.ReadOpfData(cancellationToken); } } }