Compare commits
16 commits
develop
...
feature/ge
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fad70432fb | ||
![]() |
5423526484 | ||
![]() |
6f4162d793 | ||
![]() |
9d31262448 | ||
![]() |
353d44a882 | ||
![]() |
4ccac5f479 | ||
![]() |
08cc7c7cbd | ||
![]() |
f5a31b9a02 | ||
![]() |
3e813534f9 | ||
![]() |
d29dd59964 | ||
![]() |
fc21073898 | ||
![]() |
58c77b32b1 | ||
![]() |
fc87dba0a7 | ||
![]() |
304fd8bc79 | ||
![]() |
4fa21fe1ca | ||
![]() |
42cd6e9b3a |
22 changed files with 207 additions and 43 deletions
|
@ -154,5 +154,25 @@ public class VolumeListExtensionsTests
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Single volume (comicvine type style) with negative or non-numerical
|
||||
/// </summary>
|
||||
public void GetCoverImage_LooseChapters_WithSub1_InOneVolume()
|
||||
{
|
||||
var volumes = new List<Volume>()
|
||||
{
|
||||
new VolumeBuilder("2")
|
||||
.WithChapter(new ChapterBuilder("-1").WithCoverImage("Chapter -1").Build())
|
||||
.WithChapter(new ChapterBuilder("1").WithCoverImage("Chapter 1").Build())
|
||||
.Build(),
|
||||
};
|
||||
|
||||
// Not testable due to the code not actually doing the heavy lifting
|
||||
// var actual = volumes.GetCoverImage(MangaFormat.Archive);
|
||||
// Assert.NotNull(actual);
|
||||
// Assert.Equal("Chapter 1", actual.CoverImage);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -193,6 +193,7 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
.Where(t => t.Id == tag.Id)
|
||||
.SelectMany(uc => uc.Items.Select(s => s.Metadata))
|
||||
.Select(sm => sm.AgeRating)
|
||||
.DefaultIfEmpty()
|
||||
.MaxAsync();
|
||||
tag.AgeRating = maxAgeRating;
|
||||
await _context.SaveChangesAsync();
|
||||
|
|
|
@ -34,5 +34,9 @@ public enum LibraryType
|
|||
/// </summary>
|
||||
[Description("Comic (ComicVine)")]
|
||||
ComicVine = 5,
|
||||
|
||||
/// <summary>
|
||||
/// This library requires custom regex from admin
|
||||
/// </summary>
|
||||
[Description("Generic")]
|
||||
Generic = 6,
|
||||
}
|
||||
|
|
|
@ -40,8 +40,11 @@ public class Library : IEntityDate
|
|||
/// </summary>
|
||||
/// <remarks>Scrobbling requires a valid LicenseKey</remarks>
|
||||
public bool AllowScrobbling { get; set; } = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extra Regex that can be used for parsing
|
||||
/// </summary>
|
||||
/// <remarks>This is only used for Generic Library</remarks>
|
||||
//public string? ExtraParsingRegex { get; set; }
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
|
|
|
@ -28,6 +28,7 @@ public class ReadingItemService : IReadingItemService
|
|||
private readonly ImageParser _imageParser;
|
||||
private readonly BookParser _bookParser;
|
||||
private readonly PdfParser _pdfParser;
|
||||
private readonly GenericLibraryParser _genericParser;
|
||||
|
||||
public ReadingItemService(IArchiveService archiveService, IBookService bookService, IImageService imageService,
|
||||
IDirectoryService directoryService, ILogger<ReadingItemService> logger)
|
||||
|
@ -43,6 +44,7 @@ public class ReadingItemService : IReadingItemService
|
|||
_bookParser = new BookParser(directoryService, bookService, _basicParser);
|
||||
_comicVineParser = new ComicVineParser(directoryService);
|
||||
_pdfParser = new PdfParser(directoryService);
|
||||
_genericParser = new GenericLibraryParser(directoryService);
|
||||
|
||||
}
|
||||
|
||||
|
@ -177,6 +179,10 @@ public class ReadingItemService : IReadingItemService
|
|||
/// <returns></returns>
|
||||
private ParserInfo? Parse(string path, string rootPath, string libraryRoot, LibraryType type)
|
||||
{
|
||||
if (_genericParser.IsApplicable(path, type))
|
||||
{
|
||||
return _genericParser.Parse(path, rootPath, libraryRoot, type, GetComicInfo(path));
|
||||
}
|
||||
if (_comicVineParser.IsApplicable(path, type))
|
||||
{
|
||||
return _comicVineParser.Parse(path, rootPath, libraryRoot, type, GetComicInfo(path));
|
||||
|
|
|
@ -416,15 +416,12 @@ public class ParseScannedFiles
|
|||
var folder = result.Folder;
|
||||
var libraryRoot = result.LibraryRoot;
|
||||
|
||||
// When processing files for a folder and we do enter, we need to parse the information and combine parser infos
|
||||
// NOTE: We might want to move the merge step later in the process, like return and combine.
|
||||
_logger.LogDebug("[ScannerService] Found {Count} files for {Folder}", files.Count, folder);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.FileScanProgressEvent($"{files.Count} files in {folder}", library.Name, ProgressEventType.Updated));
|
||||
if (files.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("[ScannerService] {Folder} is empty, no longer in this location, or has no file types that match Library File Types", folder);
|
||||
result.ParserInfos = ArraySegment<ParserInfo>.Empty;
|
||||
_logger.LogInformation("[ScannerService] {Folder} is empty or is no longer in this location", folder);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using API.Data.Metadata;
|
||||
using API.Entities.Enums;
|
||||
|
||||
|
@ -11,7 +12,8 @@ namespace API.Services.Tasks.Scanner.Parser;
|
|||
/// </summary>
|
||||
public class BasicParser(IDirectoryService directoryService, IDefaultParser imageParser) : DefaultParser(directoryService)
|
||||
{
|
||||
public override ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo? comicInfo = null)
|
||||
public override ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type,
|
||||
ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null)
|
||||
{
|
||||
var fileName = directoryService.FileSystem.Path.GetFileNameWithoutExtension(filePath);
|
||||
// TODO: Potential Bug: This will return null, but on Image libraries, if all images, we would want to include this.
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
using API.Data.Metadata;
|
||||
using System.Collections.Generic;
|
||||
using API.Data.Metadata;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.Services.Tasks.Scanner.Parser;
|
||||
|
||||
public class BookParser(IDirectoryService directoryService, IBookService bookService, BasicParser basicParser) : DefaultParser(directoryService)
|
||||
public class BookParser(IDirectoryService directoryService, IBookService bookService, IDefaultParser basicParser) : DefaultParser(directoryService)
|
||||
{
|
||||
public override ParserInfo Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo comicInfo = null)
|
||||
public override ParserInfo Parse(string filePath, string rootPath, string libraryRoot, LibraryType type,
|
||||
ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null)
|
||||
{
|
||||
var info = bookService.ParseInfo(filePath);
|
||||
if (info == null) return null;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using API.Data.Metadata;
|
||||
using API.Entities.Enums;
|
||||
|
@ -15,11 +16,8 @@ public class ComicVineParser(IDirectoryService directoryService) : DefaultParser
|
|||
/// <summary>
|
||||
/// This Parser generates Series name to be defined as Series + first Issue Volume, so "Batman (2020)".
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="rootPath"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public override ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo? comicInfo = null)
|
||||
public override ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type,
|
||||
ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null)
|
||||
{
|
||||
if (type != LibraryType.ComicVine) return null;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using API.Data.Metadata;
|
||||
|
@ -8,7 +9,7 @@ namespace API.Services.Tasks.Scanner.Parser;
|
|||
|
||||
public interface IDefaultParser
|
||||
{
|
||||
ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo? comicInfo = null);
|
||||
ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null);
|
||||
void ParseFromFallbackFolders(string filePath, string rootPath, LibraryType type, ref ParserInfo ret);
|
||||
bool IsApplicable(string filePath, LibraryType type);
|
||||
}
|
||||
|
@ -26,8 +27,12 @@ public abstract class DefaultParser(IDirectoryService directoryService) : IDefau
|
|||
/// <param name="filePath"></param>
|
||||
/// <param name="rootPath">Root folder</param>
|
||||
/// <param name="type">Allows different Regex to be used for parsing.</param>
|
||||
/// <param name="type">Allows different Regex to be used for parsing.</param>
|
||||
/// <param name="comicInfo">ComicInfo if present (for epub it si always present)</param>
|
||||
/// <param name="extraRegex">The regex for the Generic Parser</param>
|
||||
/// <returns><see cref="ParserInfo"/> or null if Series was empty</returns>
|
||||
public abstract ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo? comicInfo = null);
|
||||
public abstract ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type,
|
||||
ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null);
|
||||
|
||||
/// <summary>
|
||||
/// Fills out <see cref="ParserInfo"/> by trying to parse volume, chapters, and series from folders
|
||||
|
|
99
API/Services/Tasks/Scanner/Parser/GenericLibraryParser.cs
Normal file
99
API/Services/Tasks/Scanner/Parser/GenericLibraryParser.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using API.Data.Metadata;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.Services.Tasks.Scanner.Parser;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Uses an at-runtime array of Regex to parse out information
|
||||
/// </summary>
|
||||
/// <param name="directoryService"></param>
|
||||
public class GenericLibraryParser(IDirectoryService directoryService) : DefaultParser(directoryService)
|
||||
{
|
||||
public override ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type,
|
||||
ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null)
|
||||
{
|
||||
//if (extraRegex == null) return null;
|
||||
|
||||
// It can be very difficult for the user to supply all the regex needed to properly parse, we might need to let them override (but not sure how this will work)
|
||||
extraRegex = new List<string>()
|
||||
{
|
||||
@"(?<Series>.*)(\b|_)v(?<Volume>\d+-?\d+)( |_)"
|
||||
};
|
||||
|
||||
// The idea is this is passed in as a default param. Only Generic will use it
|
||||
var fileName = directoryService.FileSystem.Path.GetFileNameWithoutExtension(filePath);
|
||||
var info = new ParserInfo()
|
||||
{
|
||||
Filename = Path.GetFileName(filePath),
|
||||
Format = Parser.ParseFormat(filePath),
|
||||
Title = Parser.RemoveExtensionIfSupported(fileName)!,
|
||||
FullFilePath = filePath,
|
||||
Series = string.Empty,
|
||||
ComicInfo = comicInfo,
|
||||
Chapters = Parser.ParseComicChapter(fileName),
|
||||
Volumes = Parser.ParseComicVolume(fileName)
|
||||
};
|
||||
|
||||
|
||||
foreach (var regex in extraRegex)
|
||||
{
|
||||
var matches = new Regex(regex, Parser.MatchOptions, Parser.RegexTimeout).Matches(fileName);
|
||||
foreach (var group in matches.Select(match => match.Groups))
|
||||
{
|
||||
foreach (var matchKey in group.Keys)
|
||||
{
|
||||
var matchValue = group[matchKey].Value;
|
||||
switch (matchKey)
|
||||
{
|
||||
case "Series":
|
||||
info.Series = SetIfNotDefault(matchValue, info.Series);
|
||||
break;
|
||||
case "Chapter":
|
||||
info.Chapters = SetIfNotDefault(matchValue, info.Chapters);
|
||||
break;
|
||||
case "Volume":
|
||||
info.Volumes = SetIfNotDefault(matchValue, info.Volumes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the final info here: (cleaning values, setting internal encoding overrides)
|
||||
if (info.IsSpecial)
|
||||
{
|
||||
info.Volumes = Parser.SpecialVolume;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(info.Chapters))
|
||||
{
|
||||
info.Chapters = Parser.DefaultChapter;
|
||||
}
|
||||
|
||||
if (!info.IsSpecial && string.IsNullOrEmpty(info.Volumes))
|
||||
{
|
||||
info.Chapters = Parser.LooseLeafVolume;
|
||||
}
|
||||
|
||||
|
||||
return string.IsNullOrEmpty(info.Series) ? null : info;
|
||||
}
|
||||
|
||||
private static string SetIfNotDefault(string value, string originalValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return originalValue;
|
||||
if (string.IsNullOrEmpty(originalValue)) return value;
|
||||
|
||||
return originalValue;
|
||||
}
|
||||
|
||||
public override bool IsApplicable(string filePath, LibraryType type)
|
||||
{
|
||||
return type == LibraryType.Generic;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using API.Data.Metadata;
|
||||
using API.Entities.Enums;
|
||||
|
||||
|
@ -7,7 +8,8 @@ namespace API.Services.Tasks.Scanner.Parser;
|
|||
|
||||
public class ImageParser(IDirectoryService directoryService) : DefaultParser(directoryService)
|
||||
{
|
||||
public override ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo? comicInfo = null)
|
||||
public override ParserInfo? Parse(string filePath, string rootPath, string libraryRoot, LibraryType type,
|
||||
ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null)
|
||||
{
|
||||
if (type != LibraryType.Image || !Parser.IsImage(filePath)) return null;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ public static class Parser
|
|||
public const string SupportedExtensions =
|
||||
ArchiveFileExtensions + "|" + ImageFileExtensions + "|" + BookFileExtensions;
|
||||
|
||||
private const RegexOptions MatchOptions =
|
||||
public const RegexOptions MatchOptions =
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant;
|
||||
|
||||
private static readonly ImmutableArray<string> FormatTagSpecialKeywords = ImmutableArray.Create(
|
||||
|
@ -1149,7 +1149,7 @@ public static class Parser
|
|||
|
||||
public static string? ExtractFilename(string fileUrl)
|
||||
{
|
||||
var matches = Parser.CssImageUrlRegex.Matches(fileUrl);
|
||||
var matches = CssImageUrlRegex.Matches(fileUrl);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
if (!match.Success) continue;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using API.Data.Metadata;
|
||||
using API.Entities.Enums;
|
||||
|
||||
|
@ -6,7 +7,8 @@ namespace API.Services.Tasks.Scanner.Parser;
|
|||
|
||||
public class PdfParser(IDirectoryService directoryService) : DefaultParser(directoryService)
|
||||
{
|
||||
public override ParserInfo Parse(string filePath, string rootPath, string libraryRoot, LibraryType type, ComicInfo comicInfo = null)
|
||||
public override ParserInfo Parse(string filePath, string rootPath, string libraryRoot, LibraryType type,
|
||||
ComicInfo? comicInfo = null, IEnumerable<string>? extraRegex = null)
|
||||
{
|
||||
var fileName = directoryService.FileSystem.Path.GetFileNameWithoutExtension(filePath);
|
||||
var ret = new ParserInfo
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import {FileTypeGroup} from "./file-type-group.enum";
|
||||
|
||||
export enum LibraryType {
|
||||
Manga = 0,
|
||||
Comic = 1,
|
||||
Book = 2,
|
||||
Images = 3,
|
||||
LightNovel = 4,
|
||||
ComicVine = 5
|
||||
Manga = 0,
|
||||
Comic = 1,
|
||||
Book = 2,
|
||||
Images = 3,
|
||||
LightNovel = 4,
|
||||
ComicVine = 5,
|
||||
Generic = 6
|
||||
}
|
||||
|
||||
export interface Library {
|
||||
|
|
|
@ -26,6 +26,8 @@ export class LibraryTypePipe implements PipeTransform {
|
|||
return this.translocoService.translate('library-type-pipe.manga');
|
||||
case LibraryType.LightNovel:
|
||||
return this.translocoService.translate('library-type-pipe.lightNovel');
|
||||
case LibraryType.Generic:
|
||||
return this.translocoService.translate('library-type-pipe.generic');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ export class UtilityService {
|
|||
switch(libraryType) {
|
||||
case LibraryType.Book:
|
||||
case LibraryType.LightNovel:
|
||||
case LibraryType.Generic:
|
||||
return this.translocoService.translate('common.book-num') + (includeSpace ? ' ' : '');
|
||||
case LibraryType.Comic:
|
||||
case LibraryType.ComicVine:
|
||||
|
|
|
@ -200,6 +200,7 @@ export class SideNavComponent implements OnInit {
|
|||
getLibraryTypeIcon(format: LibraryType) {
|
||||
switch (format) {
|
||||
case LibraryType.Book:
|
||||
case LibraryType.Generic:
|
||||
case LibraryType.LightNovel:
|
||||
return 'fa-book';
|
||||
case LibraryType.Comic:
|
||||
|
|
|
@ -196,6 +196,12 @@ export class LibrarySettingsModalComponent implements OnInit {
|
|||
this.libraryForm.get(FileTypeGroup.Pdf + '')?.setValue(false);
|
||||
this.libraryForm.get(FileTypeGroup.Epub + '')?.setValue(false);
|
||||
break;
|
||||
case LibraryType.Generic:
|
||||
this.libraryForm.get(FileTypeGroup.Archive + '')?.setValue(false);
|
||||
this.libraryForm.get(FileTypeGroup.Images + '')?.setValue(false);
|
||||
this.libraryForm.get(FileTypeGroup.Pdf + '')?.setValue(true);
|
||||
this.libraryForm.get(FileTypeGroup.Epub + '')?.setValue(false);
|
||||
break;
|
||||
}
|
||||
|
||||
this.libraryForm.get('allowScrobbling')?.setValue(this.IsKavitaPlusEligible);
|
||||
|
|
|
@ -518,7 +518,8 @@
|
|||
"manga": "Manga",
|
||||
"comicVine": "ComicVine",
|
||||
"image": "Image",
|
||||
"lightNovel": "Light Novel"
|
||||
"lightNovel": "Light Novel",
|
||||
"generic": "Generic"
|
||||
},
|
||||
|
||||
"age-rating-pipe": {
|
||||
|
|
36
openapi.json
36
openapi.json
|
@ -7,7 +7,7 @@
|
|||
"name": "GPL-3.0",
|
||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||
},
|
||||
"version": "0.7.14.12"
|
||||
"version": "0.7.14.13"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
|
@ -3138,7 +3138,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -3152,7 +3153,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -3166,7 +3168,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -3854,7 +3857,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -14157,7 +14161,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -14782,7 +14787,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"description": "Library type",
|
||||
|
@ -16616,7 +16622,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -16651,6 +16658,7 @@
|
|||
},
|
||||
"created": {
|
||||
"type": "string",
|
||||
"description": "Extra Regex that can be used for parsing",
|
||||
"format": "date-time"
|
||||
},
|
||||
"lastModified": {
|
||||
|
@ -16731,7 +16739,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -17802,7 +17811,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -17856,7 +17866,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -20248,7 +20259,8 @@
|
|||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue