feat: add new volumes when found

This commit was merged in pull request #43.
This commit is contained in:
2026-08-05 20:43:49 +02:00
parent b442b8cca2
commit 9348b3374a
+478 -309
View File
@@ -269,11 +269,14 @@ final class ViewController: UIViewController, UIGestureRecognizerDelegate {
queue.async { [self] in
for dir in directories {
var foundFile = false
if !fileManager.fileExists(atPath: dir.appendingPathComponent(metadataFilename).path) {
getMetadataFromFilename(path: dir)
if pendingAlert != nil {
return
}
} else {
foundFile = true
}
currentPath = dir
@@ -288,7 +291,7 @@ final class ViewController: UIViewController, UIGestureRecognizerDelegate {
return
}
var byteBuffer = ByteBuffer(data: data)
guard let metadata: Metadata = try? getCheckedRoot(byteBuffer: &byteBuffer) else {
guard var metadata: Metadata = try? getCheckedRoot(byteBuffer: &byteBuffer) else {
DispatchQueue.main.async {
alert.message =
String(format: "Failed to read contents of metadata flatbuffer file: \(metadataPath))")
@@ -296,6 +299,35 @@ final class ViewController: UIViewController, UIGestureRecognizerDelegate {
pendingAlert = alert
return
}
if foundFile {
print("found file")
guard let archivePaths =
getArchivePaths(volumeDir: dir.appendingPathComponent("volumes"), alert: alert)
else {
return
}
let archives = archivePaths.map { $0.lastPathComponent }
var missing = false
for path in metadata.volumes {
if let nPath = path.archive {
if !archives.contains(nPath) {
missing = true
}
}
}
if missing {
getMetadataFromFilename(path: dir)
} else if archives.count != metadata.volumes.count {
guard let nM = addNewVolumesToMetadata(
pM: metadata, volumeURLs: archivePaths, volumeDir: dir.appendingPathComponent("volumes")
) else {
return
}
metadata = nM
}
}
metadataList[dir] = metadata
loadLocalState()
let volCovP = ProgressIndices(v: state[currentPath.lastPathComponent]!.progress.v, c: 0, i: 0)
@@ -418,9 +450,306 @@ final class ViewController: UIViewController, UIGestureRecognizerDelegate {
return CGSize(width: width, height: height)
}
func getArchivePaths(volumeDir: URL, alert: UIAlertController) -> [URL]? {
var isDir: ObjCBool = false
if !fileManager.fileExists(atPath: volumeDir.path, isDirectory: &isDir) || !isDir.boolValue {
alert.message = "Subdirectory volumes not found: \"\(volumeDir)\", returning"
pendingAlert = alert
return nil
}
let volumeURLs = try? fileManager.contentsOfDirectory(
at: volumeDir,
includingPropertiesForKeys: [.isRegularFileKey],
options: [.skipsHiddenFiles]
).filter { $0.pathExtension == "cbz" }
.sorted { $0.lastPathComponent < $1.lastPathComponent }
guard let result = volumeURLs else {
alert.message = String(format: "failed to read dir: \(volumeDir)")
pendingAlert = alert
return nil
}
if result.isEmpty {
alert.message = String(format: "found no volumes in volume dir: \(volumeDir)")
pendingAlert = alert
return nil
}
return result
}
func getMatchingPattern(filename: String, alert: UIAlertController) -> Pattern? {
// Beautiful, is it not?
guard let patterns: [Pattern] = try? [
Pattern(
pattern: NSRegularExpression(
pattern: #"^(.*) - c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#
),
titlePosition: 1,
volumePosition: 3,
chapterPosition: 2,
pagePosition: 4
),
Pattern(
pattern: NSRegularExpression(
pattern: "^(.*) - v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"
),
titlePosition: 1,
volumePosition: 2,
chapterPosition: 4,
pagePosition: 3
),
] else {
alert.message = String(format: "Regular expression is invalid")
pendingAlert = alert
return nil
}
var matchingPattern: Pattern?
for testPattern in patterns {
let range = NSRange(filename.startIndex..., in: filename)
if let match = testPattern.pattern.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: testPattern.titlePosition)) == "" {
continue
}
} else {
continue
}
if let match = testPattern.pattern.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: testPattern.pagePosition)) == "" {
continue
}
} else {
continue
}
if let match = testPattern.pattern.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: testPattern.chapterPosition)) == "" {
continue
}
} else {
continue
}
if let match = testPattern.pattern.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: testPattern.volumePosition)) == "" {
continue
}
} else {
continue
}
matchingPattern = testPattern
break
}
guard let pattern = matchingPattern else {
alert.message = String(format: "Could not find matching pattern, likely missing regex.")
pendingAlert = alert
return nil
}
return pattern
}
func getVolumeMetadata(
builder: inout FlatBufferBuilder, pattern: Pattern,
filenames: [(String, String)], volumeDir: URL, alert: UIAlertController
) -> ([(Offset, MetaValue)], UInt32)? {
var imageCount: UInt32 = 0
var volumes: [(Offset, MetaValue)] = []
var currentChapters: [Offset] = []
var currentImages: [Offset] = []
var previousVolumeURL = ""
var currentVolume: UInt32!
var currentChapter: (UInt32, UInt32?)!
for (i, (volumeURL, filename)) in filenames.enumerated() {
let range = NSRange(filename.startIndex..., in: filename)
if let match = pattern.pattern.firstMatch(in: filename, range: range) {
imageCount += 1
let chapterStr = (filename as NSString).substring(with: match.range(at: pattern.chapterPosition))
let chapterParts = chapterStr.split(separator: "x", maxSplits: 1)
if chapterParts.count < 1 {
alert.message = String(format: "Did not find chapter number in filename: \(filename)")
pendingAlert = alert
return nil
}
guard let nChapter0 = UInt32(chapterParts[0]) else {
alert.message = String(format: "Failed to convert chapter number to an int: \(filename)")
pendingAlert = alert
return nil
}
let chapter: (UInt32, UInt32?) = (
nChapter0, chapterParts.count > 1 ? UInt32(chapterParts[1]) : nil
)
guard let volume =
UInt32((filename as NSString)
.substring(with: match.range(at: pattern.volumePosition)))
else {
alert.message = String(format: "failed to get match for volume in filename: \(filename)")
pendingAlert = alert
return nil
}
let pageStr = (filename as NSString).substring(with: match.range(at: pattern.pagePosition))
let pageParts = pageStr.split(separator: "-", maxSplits: 1)
guard let nPage = UInt32(pageParts[0]) else {
alert.message = String(format: "failed to read archive: \(filename)")
pendingAlert = alert
return nil
}
let (page, doublePage) = (nPage, pageParts.count > 1)
let archive: Archive!
do {
let a = try Archive(url: volumeDir.appendingPathComponent(volumeURL), accessMode: .read)
archive = a
} catch {
alert.message = String(format: "failed to read archive: \(volumeURL)")
pendingAlert = alert
return nil
}
guard let entry = archive[filename] else {
alert.message = String(format: "failed to archive entry: \(filename)")
pendingAlert = alert
return nil
}
var size = ImageDimensions(width: 0, height: 0)
do {
size = try imageDimensions(of: entry, in: archive)
} catch {
alert.message = String(format: "Failed to get image dimensions of filename: \(filename)")
pendingAlert = alert
return nil
}
let m_filename = builder.create(string: filename)
let imageMetadata = ImageMetadata.createImageMetadata(
&builder,
doublePage: doublePage,
filenameOffset: m_filename,
firstPage: page,
size: Size(width: UInt32(size.width), height: UInt32(size.height))
)
if currentVolume != nil {
if volume != currentVolume {
assert(volume > currentVolume)
let chapterName = builder.create(string: "")
let imagesOffset = builder.createVector(ofOffsets: currentImages)
let chapterMetadata = ChapterMetadata.createChapterMetadata(
&builder,
chapter: MetaValue(
main: currentChapter.0, bonus: currentChapter.1 ?? 0
),
nameOffset: chapterName,
imagesVectorOffset: imagesOffset
)
currentChapters.append(chapterMetadata)
let volumeTitle = builder.create(string: "")
let volumeURLOffset = builder.create(string: previousVolumeURL)
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
&builder,
volume: MetaValue(main: currentVolume, bonus: 0),
titleOffset: volumeTitle,
archiveOffset: volumeURLOffset,
chaptersVectorOffset: chaptersOffset
)
volumes.append((volumeMetadata, MetaValue(main: currentVolume, bonus: 0)))
currentImages = [imageMetadata]
currentChapters = []
} else if chapter != currentChapter {
if chapter.0 == currentChapter.0 {
if chapter.1 == nil {
alert.message = String(format: "chapter.1 is nil")
pendingAlert = alert
return nil
}
if chapter.1! != currentChapter.1 ?? 1 {
alert.message = String(format: "chapter != currentChapter")
pendingAlert = alert
return nil
}
assert(chapter.1! == currentChapter.1 ?? 1)
} else {
if chapter.0 != currentChapter.0 + 1 {
alert.message = String(format: "chapter.0 != currentChapter.0 + 1")
pendingAlert = alert
return nil
}
assert(chapter.0 == currentChapter.0 + 1)
}
let chapterName = builder.create(string: "")
let imagesOffset = builder.createVector(ofOffsets: currentImages)
let chapterMetadata = ChapterMetadata.createChapterMetadata(
&builder,
chapter: MetaValue(
main: currentChapter.0, bonus: currentChapter.1 ?? 0
),
nameOffset: chapterName,
imagesVectorOffset: imagesOffset
)
currentChapters.append(chapterMetadata)
currentImages = [imageMetadata]
} else {
currentImages.append(imageMetadata)
}
} else {
currentImages.append(imageMetadata)
}
if i == filenames.count - 1 {
let chapterName = builder.create(string: "")
let imagesOffset = builder.createVector(ofOffsets: currentImages)
let chapterMetadata = ChapterMetadata.createChapterMetadata(
&builder,
chapter: MetaValue(
main: chapter.0, bonus: chapter.1 ?? 0
),
nameOffset: chapterName,
imagesVectorOffset: imagesOffset
)
currentChapters.append(chapterMetadata)
let volumeTitle = builder.create(string: "")
let volumeURLOffset = builder.create(string: volumeURL)
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
&builder,
volume: MetaValue(main: volume, bonus: 0),
titleOffset: volumeTitle,
archiveOffset: volumeURLOffset,
chaptersVectorOffset: chaptersOffset
)
volumes.append((volumeMetadata, MetaValue(main: volume, bonus: 0)))
currentImages = []
currentChapters = []
}
currentVolume = volume
currentChapter = chapter
previousVolumeURL = volumeURL
} else {
print("no regex match on \(filename), returning")
return nil
}
}
return (volumes, imageCount)
}
struct Pattern {
var pattern: NSRegularExpression
var title: NSRegularExpression
var titlePosition: Int
var volumePosition: Int
var chapterPosition: Int
var pagePosition: Int
@@ -434,90 +763,15 @@ final class ViewController: UIViewController, UIGestureRecognizerDelegate {
)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
// Beautiful, is it not?
var nPatterns: [Pattern]?
do {
let fPatterns: [Pattern] = try [
Pattern(
pattern: NSRegularExpression(
pattern: #"c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#
),
title: NSRegularExpression(
pattern: #"^(.*) - c([0-9]+(?:x[0-9]+)?) \(v([0-9]+)\) - p([0-9]+(?:-p?[0-9]+)?)"#
),
volumePosition: 2,
chapterPosition: 1,
pagePosition: 3
),
Pattern(
pattern: NSRegularExpression(
pattern: "v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"
),
title: NSRegularExpression(
pattern: "^(.*) - v([0-9]+) p([0-9]+(?:-[0-9]+)?) - c([0-9]+(?:x[0-9]+)?)"
),
volumePosition: 1,
chapterPosition: 3,
pagePosition: 2
),
]
nPatterns = fPatterns
} catch {
alert.message = String(format: "Regular expression is invalid")
pendingAlert = alert
return
}
guard let patterns = nPatterns else {
alert.message = String(format: "Regular expression is invalid")
pendingAlert = alert
return
}
if fileManager.fileExists(
atPath: path.appendingPathComponent(metadataFilename).absoluteString
) {
print("skipped metadata file creation")
return
}
var builder = FlatBufferBuilder(initialSize: 1024)
var volumes: [Offset] = []
var currentChapters: [Offset] = []
var currentImages: [Offset] = []
var previousVolumeURL = ""
var titleValue = ""
var currentVolume: UInt32!
var currentChapter: (UInt32, UInt32?)!
let volumeDir = path.appendingPathComponent("volumes")
var isDir: ObjCBool = false
if !fileManager.fileExists(atPath: volumeDir.path, isDirectory: &isDir) || !isDir.boolValue {
print("Subdirectory volumes not found in \"\(path)\", returning")
return
}
let volumeURLs = try? fileManager.contentsOfDirectory(
at: volumeDir,
includingPropertiesForKeys: [.isRegularFileKey],
options: [.skipsHiddenFiles]
).filter { $0.pathExtension == "cbz" }
.sorted { $0.lastPathComponent < $1.lastPathComponent }
if volumeURLs == nil {
alert.message = String(format: "failed to read dir: \(volumeDir)")
pendingAlert = alert
return
}
if volumeURLs!.isEmpty {
print("found no volumes in volume dir: \(volumeDir)")
guard let volumeURLs = getArchivePaths(volumeDir: volumeDir, alert: alert) else {
return
}
var filenames: [(String, String)] = []
for volumeURL in volumeURLs! {
for volumeURL in volumeURLs {
do {
let volumeArchive = try Archive(url: volumeURL, accessMode: .read)
volumeArchive.map { $0.path }.sorted().forEach { filenames.append((volumeURL.lastPathComponent, $0)) }
@@ -533,237 +787,23 @@ final class ViewController: UIViewController, UIGestureRecognizerDelegate {
return
}
var matchingPattern: Pattern?
var imageCount: UInt32 = 0
guard let pattern = getMatchingPattern(filename: filenames[0].1, alert: alert) else {
return
}
do {
for testPattern in patterns {
let filename = filenames[0].1
let range = NSRange(filename.startIndex..., in: filename)
if let match = testPattern.title.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: 1)) == "" {
continue
}
} else {
continue
}
if let match = testPattern.pattern.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: testPattern.pagePosition)) == "" {
continue
}
} else {
continue
}
if let match = testPattern.pattern.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: testPattern.chapterPosition)) == "" {
continue
}
} else {
continue
}
if let match = testPattern.pattern.firstMatch(in: filename, range: range) {
if (filename as NSString).substring(with: match.range(at: testPattern.volumePosition)) == "" {
continue
}
} else {
continue
}
matchingPattern = testPattern
break
}
let titleValue: String!
let range = NSRange(filenames[0].1.startIndex..., in: filenames[0].1)
if let match = pattern.pattern.firstMatch(in: filenames[0].1, range: range) {
titleValue = (filenames[0].1 as NSString).substring(with: match.range(at: pattern.titlePosition))
} else {
print("Did not find match for title, regex did not handle this case.")
return
}
guard let pattern = matchingPattern else {
alert.message = String(format: "Could not find matching pattern, likely missing regex.")
pendingAlert = alert
return
}
for (i, (volumeURL, filename)) in filenames.enumerated() {
let range = NSRange(filename.startIndex..., in: filename)
if i == 0 {
if let match = pattern.title.firstMatch(in: filename, range: range) {
titleValue = (filename as NSString).substring(with: match.range(at: 1))
} else {
print("Did not find match for title, regex did not handle this case.")
return
}
}
if let match = pattern.pattern.firstMatch(in: filename, range: range) {
imageCount += 1
let chapterStr = (filename as NSString).substring(with: match.range(at: pattern.chapterPosition))
let chapterParts = chapterStr.split(separator: "x", maxSplits: 1)
if chapterParts.count < 1 {
alert.message = String(format: "Did not find chapter number in filename: \(filename)")
pendingAlert = alert
return
}
guard let nChapter0 = UInt32(chapterParts[0]) else {
alert.message = String(format: "Failed to convert chapter number to an int: \(filename)")
pendingAlert = alert
return
}
let chapter: (UInt32, UInt32?) = (
nChapter0, chapterParts.count > 1 ? UInt32(chapterParts[1]) : nil
)
guard let volume =
UInt32((filename as NSString)
.substring(with: match.range(at: pattern.volumePosition)))
else {
alert.message = String(format: "failed to get match for volume in filename: \(filename)")
pendingAlert = alert
return
}
let pageStr = (filename as NSString).substring(with: match.range(at: pattern.pagePosition))
let pageParts = pageStr.split(separator: "-", maxSplits: 1)
guard let nPage = UInt32(pageParts[0]) else {
alert.message = String(format: "failed to read archive: \(filename)")
pendingAlert = alert
return
}
let (page, doublePage) = (
nPage, pageParts.count > 1
)
let archive = try Archive(url: volumeDir.appendingPathComponent(volumeURL), accessMode: .read)
guard let entry = archive[filename] else {
alert.message = String(format: "failed to read archive: \(filename)")
pendingAlert = alert
return
}
var size = ImageDimensions(width: 0, height: 0)
do {
size = try imageDimensions(of: entry, in: archive)
} catch {
alert.message = String(format: "Failed to get image dimensions of filename: \(filename)")
pendingAlert = alert
return
}
let m_filename = builder.create(string: filename)
let imageMetadata = ImageMetadata.createImageMetadata(
&builder,
doublePage: doublePage,
filenameOffset: m_filename,
firstPage: page,
size: Size(width: UInt32(size.width), height: UInt32(size.height))
)
if currentVolume != nil {
if volume != currentVolume {
assert(volume > currentVolume)
let chapterName = builder.create(string: "")
let imagesOffset = builder.createVector(ofOffsets: currentImages)
let chapterMetadata = ChapterMetadata.createChapterMetadata(
&builder,
chapter: MetaValue(
main: currentChapter.0, bonus: currentChapter.1 ?? 0
),
nameOffset: chapterName,
imagesVectorOffset: imagesOffset
)
currentChapters.append(chapterMetadata)
let volumeTitle = builder.create(string: "")
let volumeURLOffset = builder.create(string: previousVolumeURL)
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
&builder,
volume: MetaValue(main: currentVolume, bonus: 0),
titleOffset: volumeTitle,
archiveOffset: volumeURLOffset,
chaptersVectorOffset: chaptersOffset
)
volumes.append(volumeMetadata)
currentImages = [imageMetadata]
currentChapters = []
} else if chapter != currentChapter {
if chapter.0 == currentChapter.0 {
if chapter.1 == nil {
alert.message = String(format: "chapter.1 is nil")
pendingAlert = alert
return
}
if chapter.1! != currentChapter.1 ?? 1 {
alert.message = String(format: "chapter != currentChapter")
pendingAlert = alert
return
}
assert(chapter.1! == currentChapter.1 ?? 1)
} else {
if chapter.0 != currentChapter.0 + 1 {
alert.message = String(format: "chapter.0 != currentChapter.0 + 1")
pendingAlert = alert
return
}
assert(chapter.0 == currentChapter.0 + 1)
}
let chapterName = builder.create(string: "")
let imagesOffset = builder.createVector(ofOffsets: currentImages)
let chapterMetadata = ChapterMetadata.createChapterMetadata(
&builder,
chapter: MetaValue(
main: currentChapter.0, bonus: currentChapter.1 ?? 0
),
nameOffset: chapterName,
imagesVectorOffset: imagesOffset
)
currentChapters.append(chapterMetadata)
currentImages = [imageMetadata]
} else {
currentImages.append(imageMetadata)
}
} else {
currentImages.append(imageMetadata)
}
if i == filenames.count - 1 {
let chapterName = builder.create(string: "")
let imagesOffset = builder.createVector(ofOffsets: currentImages)
let chapterMetadata = ChapterMetadata.createChapterMetadata(
&builder,
chapter: MetaValue(
main: chapter.0, bonus: chapter.1 ?? 0
),
nameOffset: chapterName,
imagesVectorOffset: imagesOffset
)
currentChapters.append(chapterMetadata)
let volumeTitle = builder.create(string: "")
let volumeURLOffset = builder.create(string: volumeURL)
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
&builder,
volume: MetaValue(main: volume, bonus: 0),
titleOffset: volumeTitle,
archiveOffset: volumeURLOffset,
chaptersVectorOffset: chaptersOffset
)
volumes.append(volumeMetadata)
currentImages = []
currentChapters = []
}
currentVolume = volume
currentChapter = chapter
previousVolumeURL = volumeURL
} else {
print("no regex match on \(filename), returning")
return
}
}
} catch {
print("failed reading image file names")
guard let (volumes, imageCount) = getVolumeMetadata(
builder: &builder, pattern: pattern, filenames: filenames, volumeDir: volumeDir, alert: alert
) else {
return
}
if volumes.count == 0 {
@@ -776,7 +816,7 @@ final class ViewController: UIViewController, UIGestureRecognizerDelegate {
let countryOfOrigin = builder.create(string: nil)
let description = builder.create(string: nil)
let tags = builder.createVector(ofOffsets: [])
let volumeOffsets = builder.createVector(ofOffsets: volumes)
let volumeOffsets = builder.createVector(ofOffsets: volumes.map { $0.0 })
let metadata = Metadata.createMetadata(
&builder,
titleOffset: title,
@@ -2632,6 +2672,135 @@ extension ViewController {
}
comics[indexPath.item].metadata = newMetadata
}
private func addNewVolumesToMetadata(pM: Metadata, volumeURLs: [URL], volumeDir: URL) -> Metadata? {
let alert = UIAlertController(
title: "Failed to rebuild metadata",
message: "This is an alert.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
var volumes: [(Offset, MetaValue)] = []
var currentChapters: [Offset] = []
var currentImages: [Offset] = []
var builder = FlatBufferBuilder(initialSize: 1024)
for volume in pM.volumes {
for chapter in volume.chapters {
for image in chapter.images {
let m_filename = builder.create(string: image.filename)
let imageMetadata = ImageMetadata.createImageMetadata(
&builder,
doublePage: image.doublePage,
filenameOffset: m_filename,
firstPage: image.firstPage,
size: image.size
)
currentImages.append(imageMetadata)
}
let chapterName = builder.create(string: chapter.name)
let imagesOffset = builder.createVector(ofOffsets: currentImages)
let chapterMetadata = ChapterMetadata.createChapterMetadata(
&builder,
chapter: chapter.chapter,
nameOffset: chapterName,
imagesVectorOffset: imagesOffset
)
currentChapters.append(chapterMetadata)
currentImages = []
}
let volumeTitle = builder.create(string: volume.title)
let volumeURLOffset = builder.create(string: volume.archive)
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
&builder,
volume: volume.volume,
titleOffset: volumeTitle,
archiveOffset: volumeURLOffset,
chaptersVectorOffset: chaptersOffset
)
volumes.append((volumeMetadata, volume.volume))
currentChapters = []
}
var filenames: [(String, String)] = []
for volumeURL in volumeURLs {
do {
let volumeArchive = try Archive(url: volumeURL, accessMode: .read)
volumeArchive.map { $0.path }.sorted().forEach { filenames.append((volumeURL.lastPathComponent, $0)) }
} catch {
alert.message = String(format: "failed to read archive: \(volumeURL)")
pendingAlert = alert
continue
}
}
guard let pattern = getMatchingPattern(filename: filenames[0].1, alert: alert) else {
return nil
}
guard let (newVolumeOffsets, newImageCount) = getVolumeMetadata(
builder: &builder, pattern: pattern,
filenames: filenames, volumeDir: volumeDir, alert: alert
) else {
return nil
}
volumes += newVolumeOffsets
let title = builder.create(string: pM.title)
let originalLanguage = builder.create(string: pM.originalLanguage)
let countryOfOrigin = builder.create(string: pM.countryOfOrigin)
let description = builder.create(string: pM.description)
let tags = builder.createVector(ofOffsets: [])
let volumeOffsets = builder.createVector(ofOffsets: volumes.sorted {
($0.1.main < $1.1.main) || ($0.1.main == $1.1.main && $0.1.bonus < $1.1.bonus)
}.map { $0.0 })
let metadata = Metadata.createMetadata(
&builder,
titleOffset: title,
format: pM.format,
originalLanguageOffset: originalLanguage,
countryOfOriginOffset: countryOfOrigin,
publicationDemographic: pM.publicationDemographic,
status: pM.status,
contentRating: pM.contentRating,
source: pM.source,
startReleaseDate: pM.startReleaseDate,
endReleaseDate: pM.endReleaseDate,
tagsVectorOffset: tags,
descriptionOffset: description,
anilistId: pM.anilistId,
volumesVectorOffset: volumeOffsets,
imageCount: pM.imageCount + newImageCount
)
builder.finish(offset: metadata)
do {
try builder.data.write(to: currentPath.appendingPathComponent(metadataFilename))
} catch {
alert.message = String(format: "Failed to write metadata file at: \(metadataFilename)")
DispatchQueue.main.async {
self.present(alert, animated: false)
}
}
let metadataPath = currentPath.appendingPathComponent(metadataFilename)
guard let data = try? Data(contentsOf: metadataPath) else {
alert.message = String(format: "Failed to read metadata file at: \(metadataPath)")
DispatchQueue.main.async {
self.present(alert, animated: false)
}
return nil
}
var byteBuffer = ByteBuffer(data: data)
guard let newMetadata: Metadata = try? getCheckedRoot(byteBuffer: &byteBuffer) else {
alert.message = String(format: "Failed to load flatbuffer file at: \(metadataPath)")
DispatchQueue.main.async {
self.present(alert, animated: false)
}
return nil
}
return newMetadata
}
}
extension ViewController: UIPopoverPresentationControllerDelegate {