mirror of
https://github.com/jellyfin/jellyfin-plugin-bookshelf.git
synced 2024-11-23 05:39:51 +00:00
Merge pull request #42 from jellyfin/fix-build
This commit is contained in:
commit
20c78b2e7f
@ -17,8 +17,6 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
private const string StandardOpfFile = "content.opf";
|
||||
private const string CalibreOpfFile = "metadata.opf";
|
||||
|
||||
private const string DcNamespace = @"http://purl.org/dc/elements/1.1/";
|
||||
private const string OpfNamespace = @"http://www.idpf.org/2007/opf";
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly ILogger<BookProviderFromOpf> _logger;
|
||||
@ -94,7 +92,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(metaFile);
|
||||
|
||||
OpfReader.ReadOpfData(bookResult, doc, cancellationToken, _logger);
|
||||
OpfReader.ReadOpfData(bookResult, doc, _logger, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,17 +37,17 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
public string Name => "Comic Book Zip Archive Cover Extractor";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
|
||||
public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
|
||||
{
|
||||
// Check if the file is a .cbz file
|
||||
var extension = Path.GetExtension(item.Path);
|
||||
if (string.Equals(extension, CbzFileExtension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return await LoadCover(item);
|
||||
return LoadCover(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DynamicImageResponse { HasImage = false };
|
||||
return Task.FromResult(new DynamicImageResponse { HasImage = false });
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
var (cover, imageFormat) = FindCoverEntryInZip(archive) ?? throw new InvalidOperationException("No supported cover found");
|
||||
|
||||
// Copy our cover to memory stream
|
||||
await cover.Open().CopyToAsync(memoryStream);
|
||||
await cover.Open().CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
|
||||
// Reset stream position after copying
|
||||
memoryStream.Position = 0;
|
||||
@ -117,7 +117,10 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
// There are comics with a cover file, but others with varying names for the cover
|
||||
// e.g. attackontitan_vol1_Page_001 with no indication that this is the cover except
|
||||
// that it is the first jpeg entry (and page)
|
||||
var cover = archive.GetEntry("cover" + extension) ?? archive.Entries.OrderBy(x => x.Name).FirstOrDefault(x => x.Name.EndsWith(extension));
|
||||
var cover = archive.GetEntry("cover" + extension)
|
||||
?? archive.Entries
|
||||
.OrderBy(x => x.Name)
|
||||
.FirstOrDefault(x => x.Name.EndsWith(extension, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// If we have found something, return immediately
|
||||
if (cover is not null)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Jellyfin.Plugin.Bookshelf.Providers.ComicBookInfo
|
||||
@ -90,13 +91,13 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicBookInfo
|
||||
/// Gets or sets the list of credits.
|
||||
/// </summary>
|
||||
[JsonPropertyName("credits")]
|
||||
public ComicBookInfoCredit[] Credits { get; set; } = Array.Empty<ComicBookInfoCredit>();
|
||||
public IReadOnlyList<ComicBookInfoCredit> Credits { get; set; } = Array.Empty<ComicBookInfoCredit>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of tags.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tags")]
|
||||
public string[] Tags { get; set; } = Array.Empty<string>();
|
||||
public IReadOnlyList<string> Tags { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comments.
|
||||
|
@ -48,7 +48,9 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicBookInfo
|
||||
|
||||
try
|
||||
{
|
||||
#pragma warning disable CA2007
|
||||
await using Stream stream = File.OpenRead(path);
|
||||
#pragma warning restore CA2007
|
||||
|
||||
// not yet async: https://github.com/adamhathcock/sharpcompress/pull/565
|
||||
using var archive = ZipArchive.Open(stream);
|
||||
@ -58,8 +60,12 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicBookInfo
|
||||
var volume = archive.Volumes.First();
|
||||
if (volume.Comment is not null)
|
||||
{
|
||||
#pragma warning disable CA2007
|
||||
await using var jsonStream = new MemoryStream(Encoding.UTF8.GetBytes(volume.Comment));
|
||||
var comicBookMetadata = await JsonSerializer.DeserializeAsync<ComicBookInfoFormat>(jsonStream, JsonDefaults.Options, cancellationToken);
|
||||
#pragma warning restore CA2007
|
||||
|
||||
var comicBookMetadata = await JsonSerializer.DeserializeAsync<ComicBookInfoFormat>(jsonStream, JsonDefaults.Options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (comicBookMetadata is null)
|
||||
{
|
||||
@ -120,7 +126,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicBookInfo
|
||||
metadataResult.ResultLanguage = ReadCultureInfoAsThreeLetterIsoInto(comic.Metadata.Language);
|
||||
}
|
||||
|
||||
if (comic.Metadata.Credits.Length > 0)
|
||||
if (comic.Metadata.Credits.Count > 0)
|
||||
{
|
||||
foreach (var person in comic.Metadata.Credits)
|
||||
{
|
||||
@ -151,25 +157,25 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicBookInfo
|
||||
if (comic.PublicationYear is not null)
|
||||
{
|
||||
book.ProductionYear = comic.PublicationYear;
|
||||
hasFoundMetadata |= true;
|
||||
hasFoundMetadata = true;
|
||||
}
|
||||
|
||||
if (comic.Issue is not null)
|
||||
{
|
||||
book.IndexNumber = comic.Issue;
|
||||
hasFoundMetadata |= true;
|
||||
hasFoundMetadata = true;
|
||||
}
|
||||
|
||||
if (comic.Tags is not null && comic.Tags.Length > 0)
|
||||
if (comic.Tags.Count > 0)
|
||||
{
|
||||
book.Tags = comic.Tags;
|
||||
hasFoundMetadata |= true;
|
||||
book.Tags = comic.Tags.ToArray();
|
||||
hasFoundMetadata = true;
|
||||
}
|
||||
|
||||
if (comic.PublicationYear is not null && comic.PublicationMonth is not null)
|
||||
{
|
||||
book.PremiereDate = ReadTwoPartDateInto(comic.PublicationYear.Value, comic.PublicationMonth.Value);
|
||||
hasFoundMetadata |= true;
|
||||
hasFoundMetadata = true;
|
||||
}
|
||||
|
||||
if (hasFoundMetadata)
|
||||
|
@ -30,7 +30,8 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
{
|
||||
foreach (IComicFileProvider iComicFileProvider in _comicFileProviders)
|
||||
{
|
||||
var metadata = await iComicFileProvider.ReadMetadata(info, directoryService, cancellationToken);
|
||||
var metadata = await iComicFileProvider.ReadMetadata(info, directoryService, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (metadata.HasMetadata)
|
||||
{
|
||||
@ -41,7 +42,8 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
return new MetadataResult<Book> { HasMetadata = false };
|
||||
}
|
||||
|
||||
bool IHasItemChangeMonitor.HasChanged(BaseItem item, IDirectoryService directoryService)
|
||||
/// <inheritdoc />
|
||||
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
|
||||
{
|
||||
foreach (IComicFileProvider iComicFileProvider in _comicFileProviders)
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicInfo
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<MetadataResult<Book>> ReadMetadata(ItemInfo info, IDirectoryService directoryService, CancellationToken cancellationToken)
|
||||
{
|
||||
var comicInfoXml = await LoadXml(info, directoryService, cancellationToken);
|
||||
var comicInfoXml = await LoadXml(info, directoryService, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (comicInfoXml is null)
|
||||
{
|
||||
@ -88,7 +88,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicInfo
|
||||
var comicInfoXml = XDocument.LoadAsync(reader, LoadOptions.None, cancellationToken);
|
||||
|
||||
// Read data from XML
|
||||
return await comicInfoXml;
|
||||
return await comicInfoXml.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicInfo
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<MetadataResult<Book>> ReadMetadata(ItemInfo info, IDirectoryService directoryService, CancellationToken cancellationToken)
|
||||
{
|
||||
var comicInfoXml = await LoadXml(info, directoryService, cancellationToken);
|
||||
var comicInfoXml = await LoadXml(info, directoryService, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (comicInfoXml is null)
|
||||
{
|
||||
@ -96,11 +96,14 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.ComicInfo
|
||||
}
|
||||
|
||||
// Open the xml
|
||||
#pragma warning disable CA2007
|
||||
await using var containerStream = container.Open();
|
||||
#pragma warning restore CA2007
|
||||
|
||||
var comicInfoXml = XDocument.LoadAsync(containerStream, LoadOptions.None, cancellationToken);
|
||||
|
||||
// Read data from XML
|
||||
return await comicInfoXml;
|
||||
return await comicInfoXml.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -129,12 +129,12 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.Epub
|
||||
return null;
|
||||
}
|
||||
|
||||
private Task<DynamicImageResponse> LoadCover(ZipArchive epub, XmlDocument opf, string opfRootDirectory)
|
||||
private async Task<DynamicImageResponse> LoadCover(ZipArchive epub, XmlDocument opf, string opfRootDirectory)
|
||||
{
|
||||
var coverRef = ReadCoverPath(opf, opfRootDirectory);
|
||||
if (coverRef == null)
|
||||
{
|
||||
return Task.FromResult(new DynamicImageResponse { HasImage = false });
|
||||
return new DynamicImageResponse { HasImage = false };
|
||||
}
|
||||
|
||||
var cover = coverRef.Value;
|
||||
@ -142,13 +142,14 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.Epub
|
||||
var coverFile = epub.GetEntry(cover.Path);
|
||||
if (coverFile == null)
|
||||
{
|
||||
return Task.FromResult(new DynamicImageResponse { HasImage = false });
|
||||
return new DynamicImageResponse { HasImage = false };
|
||||
}
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
using (var coverStream = coverFile.Open())
|
||||
{
|
||||
coverStream.CopyTo(memoryStream);
|
||||
await coverStream.CopyToAsync(memoryStream)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
@ -160,7 +161,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.Epub
|
||||
};
|
||||
response.SetFormatFromMimeType(cover.MimeType);
|
||||
|
||||
return Task.FromResult(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
private Task<DynamicImageResponse> GetFromZip(BaseItem item)
|
||||
|
@ -95,7 +95,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.Epub
|
||||
var opfDocument = new XmlDocument();
|
||||
opfDocument.Load(opfStream);
|
||||
|
||||
OpfReader.ReadOpfData(result, opfDocument, cancellationToken, _logger);
|
||||
OpfReader.ReadOpfData(result, opfDocument, _logger, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
@ -57,7 +58,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
return list;
|
||||
}
|
||||
|
||||
var bookResult = await FetchBookData(googleBookId, cancellationToken);
|
||||
var bookResult = await FetchBookData(googleBookId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (bookResult == null)
|
||||
{
|
||||
@ -77,12 +78,16 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var url = string.Format(GoogleApiUrls.DetailsUrl, googleBookId);
|
||||
var url = string.Format(CultureInfo.InvariantCulture, GoogleApiUrls.DetailsUrl, googleBookId);
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
|
||||
|
||||
using var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
#pragma warning disable CA2007
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
#pragma warning restore CA2007
|
||||
|
||||
return await JsonSerializer.DeserializeAsync<BookResult>(stream, JsonDefaults.Options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
@ -73,12 +74,12 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
public string Name => "Google Books";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BookInfo item, CancellationToken cancellationToken)
|
||||
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BookInfo searchInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var list = new List<RemoteSearchResult>();
|
||||
|
||||
var searchResults = await GetSearchResultsInternal(item, cancellationToken);
|
||||
var searchResults = await GetSearchResultsInternal(searchInfo, cancellationToken).ConfigureAwait(false);
|
||||
if (searchResults is null)
|
||||
{
|
||||
return Enumerable.Empty<RemoteSearchResult>();
|
||||
@ -105,21 +106,21 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MetadataResult<Book>> GetMetadata(BookInfo item, CancellationToken cancellationToken)
|
||||
public async Task<MetadataResult<Book>> GetMetadata(BookInfo info, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var metadataResult = new MetadataResult<Book>();
|
||||
metadataResult.HasMetadata = false;
|
||||
|
||||
var googleBookId = item.GetProviderId("GoogleBooks")
|
||||
?? await FetchBookId(item, cancellationToken).ConfigureAwait(false);
|
||||
var googleBookId = info.GetProviderId("GoogleBooks")
|
||||
?? await FetchBookId(info, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (string.IsNullOrEmpty(googleBookId))
|
||||
{
|
||||
return metadataResult;
|
||||
}
|
||||
|
||||
var bookResult = await FetchBookData(googleBookId, cancellationToken);
|
||||
var bookResult = await FetchBookData(googleBookId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (bookResult == null)
|
||||
{
|
||||
@ -153,12 +154,16 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
// year can be included for better results
|
||||
GetBookMetadata(item);
|
||||
|
||||
var url = string.Format(GoogleApiUrls.SearchUrl, WebUtility.UrlEncode(item.Name), 0, 20);
|
||||
var url = string.Format(CultureInfo.InvariantCulture, GoogleApiUrls.SearchUrl, WebUtility.UrlEncode(item.Name), 0, 20);
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
|
||||
|
||||
using var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
#pragma warning disable CA2007
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
#pragma warning restore CA2007
|
||||
|
||||
return await JsonSerializer.DeserializeAsync<SearchResult>(stream, JsonDefaults.Options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -166,7 +171,8 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var searchResults = await GetSearchResultsInternal(item, cancellationToken);
|
||||
var searchResults = await GetSearchResultsInternal(item, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (searchResults?.Items == null)
|
||||
{
|
||||
return null;
|
||||
@ -181,7 +187,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
}
|
||||
|
||||
// no match so move on to the next item
|
||||
if (!GetComparableName(i.VolumeInfo.Title).Equals(comparableName))
|
||||
if (!GetComparableName(i.VolumeInfo.Title).Equals(comparableName, StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -209,12 +215,16 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var url = string.Format(GoogleApiUrls.DetailsUrl, googleBookId);
|
||||
var url = string.Format(CultureInfo.InvariantCulture, GoogleApiUrls.DetailsUrl, googleBookId);
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
|
||||
|
||||
using var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
#pragma warning disable CA2007
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
#pragma warning restore CA2007
|
||||
|
||||
return await JsonSerializer.DeserializeAsync<BookResult>(stream, JsonDefaults.Options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -233,8 +243,8 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
try
|
||||
{
|
||||
book.ProductionYear = bookResult.VolumeInfo.PublishedDate?.Length > 4
|
||||
? Convert.ToInt32(bookResult.VolumeInfo.PublishedDate[..4])
|
||||
: Convert.ToInt32(bookResult.VolumeInfo.PublishedDate);
|
||||
? Convert.ToInt32(bookResult.VolumeInfo.PublishedDate[..4], CultureInfo.InvariantCulture)
|
||||
: Convert.ToInt32(bookResult.VolumeInfo.PublishedDate, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@ -252,7 +262,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
tags.Add(bookResult.VolumeInfo.MainCategory);
|
||||
}
|
||||
|
||||
if (bookResult.VolumeInfo.Categories is { Length: > 0 })
|
||||
if (bookResult.VolumeInfo.Categories is { Count: > 0 })
|
||||
{
|
||||
foreach (var category in bookResult.VolumeInfo.Categories)
|
||||
{
|
||||
@ -284,12 +294,12 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
name = name.ToLower();
|
||||
name = name.ToLower(CultureInfo.InvariantCulture);
|
||||
name = name.Normalize(NormalizationForm.FormKD);
|
||||
|
||||
foreach (var pair in _replaceEndNumerals)
|
||||
{
|
||||
if (name.EndsWith(pair.Key))
|
||||
if (name.EndsWith(pair.Key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
name = name.Remove(name.IndexOf(pair.Key, StringComparison.InvariantCulture), pair.Key.Length);
|
||||
name += pair.Value;
|
||||
@ -303,13 +313,13 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
{
|
||||
// skip char modifier and diacritics
|
||||
}
|
||||
else if (Remove.IndexOf(c) > -1)
|
||||
else if (Remove.IndexOf(c, StringComparison.Ordinal) > -1)
|
||||
{
|
||||
// skip chars we are removing
|
||||
}
|
||||
else if (Spacers.IndexOf(c) > -1)
|
||||
else if (Spacers.IndexOf(c, StringComparison.Ordinal) > -1)
|
||||
{
|
||||
sb.Append(" ");
|
||||
sb.Append(' ');
|
||||
}
|
||||
else if (c == '&')
|
||||
{
|
||||
@ -322,8 +332,8 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
}
|
||||
|
||||
name = sb.ToString();
|
||||
name = name.Replace("the", " ");
|
||||
name = name.Replace(" - ", ": ");
|
||||
name = name.Replace("the", " ", StringComparison.OrdinalIgnoreCase);
|
||||
name = name.Replace(" - ", ": ", StringComparison.Ordinal);
|
||||
|
||||
var regex = new Regex(@"\s+");
|
||||
name = regex.Replace(name, " ");
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
@ -24,6 +25,6 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
/// Gets or sets the list of items.
|
||||
/// </summary>
|
||||
[JsonPropertyName("items")]
|
||||
public BookResult[] Items { get; set; } = Array.Empty<BookResult>();
|
||||
public IReadOnlyList<BookResult> Items { get; set; } = Array.Empty<BookResult>();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
@ -18,7 +19,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
/// Gets or sets the list of authors.
|
||||
/// </summary>
|
||||
[JsonPropertyName("authors")]
|
||||
public string[] Authors { get; set; } = Array.Empty<string>();
|
||||
public IReadOnlyList<string> Authors { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the published date.
|
||||
@ -54,7 +55,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.GoogleBooks
|
||||
/// Gets or sets the list of categories.
|
||||
/// </summary>
|
||||
[JsonPropertyName("categories")]
|
||||
public string[] Categories { get; set; } = Array.Empty<string>();
|
||||
public IReadOnlyList<string> Categories { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the average rating.
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
@ -22,14 +23,14 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
/// </summary>
|
||||
/// <param name="bookResult">The metadata result to update.</param>
|
||||
/// <param name="doc">The xdocument to parse.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{TCategoryName}"/> interface.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <typeparam name="TCategoryName">The type of category.</typeparam>
|
||||
public static void ReadOpfData<TCategoryName>(
|
||||
MetadataResult<Book> bookResult,
|
||||
XmlDocument doc,
|
||||
CancellationToken cancellationToken,
|
||||
ILogger<TCategoryName> logger)
|
||||
ILogger<TCategoryName> logger,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var book = bookResult.Item;
|
||||
|
||||
@ -100,7 +101,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
{
|
||||
try
|
||||
{
|
||||
book.IndexNumber = Convert.ToInt32(seriesIndexNode.Attributes["content"]?.Value);
|
||||
book.IndexNumber = Convert.ToInt32(seriesIndexNode.Attributes["content"]?.Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@ -128,7 +129,7 @@ namespace Jellyfin.Plugin.Bookshelf.Providers
|
||||
{
|
||||
try
|
||||
{
|
||||
book.CommunityRating = Convert.ToInt32(ratingNode.Attributes["content"]?.Value);
|
||||
book.CommunityRating = Convert.ToInt32(ratingNode.Attributes["content"]?.Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -1,13 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="Rules for Jellyfin" Description="Code analysis rules for Jellyfin" ToolsVersion="14.0">
|
||||
<RuleSet Name="Rules for Jellyfin.Server" Description="Code analysis rules for Jellyfin.Server.csproj" ToolsVersion="14.0">
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
|
||||
<!-- disable warning SA1202: 'public' members must come before 'private' members -->
|
||||
<Rule Id="SA1202" Action="Info" />
|
||||
<!-- disable warning SA1204: Static members must appear before non-static members -->
|
||||
<Rule Id="SA1204" Action="Info" />
|
||||
<!-- disable warning SA1404: Code analysis suppression should have justification -->
|
||||
<Rule Id="SA1404" Action="Info" />
|
||||
|
||||
<!-- disable warning SA1009: Closing parenthesis should be followed by a space. -->
|
||||
<Rule Id="SA1009" Action="None" />
|
||||
<!-- disable warning SA1011: Closing square bracket should be followed by a space. -->
|
||||
@ -16,12 +9,18 @@
|
||||
<Rule Id="SA1101" Action="None" />
|
||||
<!-- disable warning SA1108: Block statements should not contain embedded comments -->
|
||||
<Rule Id="SA1108" Action="None" />
|
||||
<!-- disable warning SA1118: Parameter must not span multiple lines. -->
|
||||
<Rule Id="SA1118" Action="None" />
|
||||
<!-- disable warning SA1128:: Put constructor initializers on their own line -->
|
||||
<Rule Id="SA1128" Action="None" />
|
||||
<!-- disable warning SA1130: Use lambda syntax -->
|
||||
<Rule Id="SA1130" Action="None" />
|
||||
<!-- disable warning SA1200: 'using' directive must appear within a namespace declaration -->
|
||||
<Rule Id="SA1200" Action="None" />
|
||||
<!-- disable warning SA1202: 'public' members must come before 'private' members -->
|
||||
<Rule Id="SA1202" Action="None" />
|
||||
<!-- disable warning SA1204: Static members must appear before non-static members -->
|
||||
<Rule Id="SA1204" Action="None" />
|
||||
<!-- disable warning SA1309: Fields must not begin with an underscore -->
|
||||
<Rule Id="SA1309" Action="None" />
|
||||
<!-- disable warning SA1413: Use trailing comma in multi-line initializers -->
|
||||
@ -39,20 +38,51 @@
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="Microsoft.CodeAnalysis.NetAnalyzers" RuleNamespace="Microsoft.Design">
|
||||
<!-- error on CA1063: Implement IDisposable correctly -->
|
||||
<Rule Id="CA1063" Action="Error" />
|
||||
<!-- error on CA1305: Specify IFormatProvider -->
|
||||
<Rule Id="CA1305" Action="Error" />
|
||||
<!-- error on CA1307: Specify StringComparison for clarity -->
|
||||
<Rule Id="CA1307" Action="Error" />
|
||||
<!-- error on CA1309: Use ordinal StringComparison -->
|
||||
<Rule Id="CA1309" Action="Error" />
|
||||
<!-- error on CA1725: Parameter names should match base declaration -->
|
||||
<Rule Id="CA1725" Action="Error" />
|
||||
<!-- error on CA1725: Call async methods when in an async method -->
|
||||
<Rule Id="CA1727" Action="Error" />
|
||||
<!-- error on CA1813: Avoid unsealed attributes -->
|
||||
<Rule Id="CA1813" Action="Error" />
|
||||
<!-- error on CA1843: Do not use 'WaitAll' with a single task -->
|
||||
<Rule Id="CA1843" Action="Error" />
|
||||
<!-- error on CA1845: Use span-based 'string.Concat' -->
|
||||
<Rule Id="CA1845" Action="Error" />
|
||||
<!-- error on CA2016: Forward the CancellationToken parameter to methods that take one
|
||||
or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token -->
|
||||
<Rule Id="CA2016" Action="Error" />
|
||||
<!-- error on CA2254: Template should be a static expression -->
|
||||
<Rule Id="CA2254" Action="Error" />
|
||||
|
||||
<!-- disable warning CA1014: Mark assemblies with CLSCompliantAttribute -->
|
||||
<Rule Id="CA1014" Action="Info" />
|
||||
<!-- disable warning CA1024: Use properties where appropriate -->
|
||||
<Rule Id="CA1024" Action="Info" />
|
||||
<!-- disable warning CA1031: Do not catch general exception types -->
|
||||
<Rule Id="CA1031" Action="Info" />
|
||||
<!-- disable warning CA1032: Implement standard exception constructors -->
|
||||
<Rule Id="CA1032" Action="Info" />
|
||||
<!-- disable warning CA1040: Avoid empty interfaces -->
|
||||
<Rule Id="CA1040" Action="Info" />
|
||||
<!-- disable warning CA1062: Validate arguments of public methods -->
|
||||
<Rule Id="CA1062" Action="Info" />
|
||||
<!-- TODO: enable when false positives are fixed -->
|
||||
<!-- disable warning CA1508: Avoid dead conditional code -->
|
||||
<Rule Id="CA1508" Action="Info" />
|
||||
<!-- disable warning CA1716: Identifiers should not match keywords -->
|
||||
<Rule Id="CA1716" Action="Info" />
|
||||
<!-- disable warning CA1720: Identifiers should not contain type names -->
|
||||
<Rule Id="CA1720" Action="Info" />
|
||||
<!-- disable warning CA1724: Type names should not match namespaces -->
|
||||
<Rule Id="CA1724" Action="Info" />
|
||||
<!-- disable warning CA1805: Do not initialize unnecessarily -->
|
||||
<Rule Id="CA1805" Action="Info" />
|
||||
<!-- disable warning CA1812: internal class that is apparently never instantiated.
|
||||
@ -63,11 +93,11 @@
|
||||
<Rule Id="CA1822" Action="Info" />
|
||||
<!-- disable warning CA2000: Dispose objects before losing scope -->
|
||||
<Rule Id="CA2000" Action="Info" />
|
||||
<!-- disable warning CA2253: Named placeholders should not be numeric values -->
|
||||
<Rule Id="CA2253" Action="Info" />
|
||||
<!-- disable warning CA5394: Do not use insecure randomness -->
|
||||
<Rule Id="CA5394" Action="Info" />
|
||||
|
||||
<!-- disable warning CA1014: Mark assemblies with CLSCompliantAttribute -->
|
||||
<Rule Id="CA1014" Action="Info" />
|
||||
<!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
|
||||
<Rule Id="CA1054" Action="None" />
|
||||
<!-- disable warning CA1055: URI return values should not be strings -->
|
||||
@ -78,5 +108,11 @@
|
||||
<Rule Id="CA1303" Action="None" />
|
||||
<!-- disable warning CA1308: Normalize strings to uppercase -->
|
||||
<Rule Id="CA1308" Action="None" />
|
||||
<!-- disable warning CA1848: Use the LoggerMessage delegates -->
|
||||
<Rule Id="CA1848" Action="None" />
|
||||
<!-- disable warning CA2101: Specify marshaling for P/Invoke string arguments -->
|
||||
<Rule Id="CA2101" Action="None" />
|
||||
<!-- disable warning CA2234: Pass System.Uri objects instead of strings -->
|
||||
<Rule Id="CA2234" Action="None" />
|
||||
</Rules>
|
||||
</RuleSet>
|
||||
|
Loading…
Reference in New Issue
Block a user