refactor: Change structure of metadata
This commit is contained in:
+317
-242
@@ -1,13 +1,10 @@
|
|||||||
//TODO: Support for bonus chapters
|
|
||||||
//TODO: Anilist support?
|
//TODO: Anilist support?
|
||||||
//TODO: Properly avoid swallowing of input from UICollectionView used for scrolling
|
//TODO: Properly avoid swallowing of input from UICollectionView used for scrolling
|
||||||
//TODO: Convert between state for normal and scrolling page turn
|
//TODO: Convert between state for normal and scrolling page turn
|
||||||
//TODO: Support reading with scrolling and landscape mode
|
//TODO: Support reading with scrolling and landscape mode
|
||||||
//TODO: Support Cover being zeroth page.
|
|
||||||
//TODO: Support double pages.
|
|
||||||
//TODO: Do file name parsing.
|
|
||||||
//FIXME: Update comicCollectionView when switching between landscape and portrait mode
|
//FIXME: Update comicCollectionView when switching between landscape and portrait mode
|
||||||
|
|
||||||
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
let preloadCount = 2
|
let preloadCount = 2
|
||||||
@@ -20,11 +17,17 @@ enum PageTurn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ReadProgress: Codable {
|
enum ReadProgress: Codable {
|
||||||
case leftToRight(chapter: Int, page: Int)
|
case leftToRight(volumeIndex: Int, chapterIndex: Int, imageIndex: Int)
|
||||||
case rightToLeft(chapter: Int, page: Int)
|
case rightToLeft(volumeIndex: Int, chapterIndex: Int, imageIndex: Int)
|
||||||
case scroll(CGPoint)
|
case scroll(CGPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ProgressIndices {
|
||||||
|
var v: Int
|
||||||
|
var c: Int
|
||||||
|
var i: Int
|
||||||
|
}
|
||||||
|
|
||||||
enum PageTurnMode: Codable {
|
enum PageTurnMode: Codable {
|
||||||
case leftToRight
|
case leftToRight
|
||||||
case rightToLeft
|
case rightToLeft
|
||||||
@@ -46,11 +49,12 @@ struct LocalState: Codable {
|
|||||||
var backgroundColor: String = "black"
|
var backgroundColor: String = "black"
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Metadata: Decodable {
|
struct Metadata: Codable {
|
||||||
var title: String
|
var title: String
|
||||||
var original_language: String
|
var original_language: String
|
||||||
var last_volume: Float
|
var last_volume: MetaValue
|
||||||
var last_chapter: Float
|
var last_chapter: MetaValue
|
||||||
|
var chapter_count: Int
|
||||||
var publication_demographic: String
|
var publication_demographic: String
|
||||||
var status: String
|
var status: String
|
||||||
var year: Int?
|
var year: Int?
|
||||||
@@ -58,14 +62,30 @@ struct Metadata: Decodable {
|
|||||||
var state: String
|
var state: String
|
||||||
var created_at: String
|
var created_at: String
|
||||||
var updated_at: String
|
var updated_at: String
|
||||||
|
var volumes: [VolumeMetadata]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VolumeMetadata: Codable {
|
||||||
|
var volume: MetaValue
|
||||||
|
var name: String?
|
||||||
var chapters: [ChapterMetadata]
|
var chapters: [ChapterMetadata]
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChapterMetadata: Decodable {
|
struct ChapterMetadata: Codable {
|
||||||
var chapter: Float
|
var chapter: MetaValue
|
||||||
var volume: Float
|
var name: String?
|
||||||
var title: String?
|
var images: [ImageMetadata]
|
||||||
var pages: Int
|
}
|
||||||
|
|
||||||
|
struct ImageMetadata: Codable {
|
||||||
|
var doublePage: Bool
|
||||||
|
var fileName: String
|
||||||
|
var firstPage: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MetaValue: Codable {
|
||||||
|
var main: Int
|
||||||
|
var bonus: Int?
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||||
@@ -84,7 +104,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
var mode = PageTurnMode.leftToRight
|
var mode = PageTurnMode.leftToRight
|
||||||
var metadata: Metadata!
|
var metadata: Metadata!
|
||||||
var currentPage: Int! = nil
|
var currentPage: Int! = nil
|
||||||
var currentChapterIndex: Int! = nil
|
var progress = ProgressIndices.init(v: 0, c: 0, i: 0)
|
||||||
var currentPath: URL!
|
var currentPath: URL!
|
||||||
|
|
||||||
var changedOrientation = false
|
var changedOrientation = false
|
||||||
@@ -161,6 +181,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
setupHomeView()
|
setupHomeView()
|
||||||
if let name = globalState.comicName {
|
if let name = globalState.comicName {
|
||||||
readComic(name: name)
|
readComic(name: name)
|
||||||
|
} else {
|
||||||
|
print("no comic?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +219,14 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
}.sorted { $0.path < $1.path }
|
}.sorted { $0.path < $1.path }
|
||||||
|
|
||||||
for dir in directories {
|
for dir in directories {
|
||||||
|
if !fileManager.fileExists(
|
||||||
|
atPath: dir.appendingPathComponent(
|
||||||
|
"metadata.json"
|
||||||
|
).path)
|
||||||
|
{
|
||||||
|
getMetadataFromFileName(path: dir)
|
||||||
|
}
|
||||||
|
|
||||||
comics.append(
|
comics.append(
|
||||||
Comic(
|
Comic(
|
||||||
cover:
|
cover:
|
||||||
@@ -252,6 +282,143 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMetadataFromFileName(path: URL) {
|
||||||
|
// Beautiful, is it not?
|
||||||
|
let pattern = "c([0-9]+(?:x[0-9]+)?) \\(v([0-9]+)\\) - p([0-9]+(?:-[0-9]+)?)"
|
||||||
|
|
||||||
|
var currentVolume: Int! = nil
|
||||||
|
var currentChapter: (Int, Int?)!
|
||||||
|
|
||||||
|
let outFileName = "metadata.json"
|
||||||
|
|
||||||
|
if fileManager.fileExists(
|
||||||
|
atPath: path.appendingPathComponent(outFileName).absoluteString)
|
||||||
|
{
|
||||||
|
print("skipped metadata file creation")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadata = Metadata.init(
|
||||||
|
title: "",
|
||||||
|
original_language: "",
|
||||||
|
last_volume: MetaValue.init(main: 0, bonus: nil),
|
||||||
|
last_chapter: MetaValue.init(main: 0, bonus: nil),
|
||||||
|
chapter_count: 0,
|
||||||
|
publication_demographic: "",
|
||||||
|
status: "",
|
||||||
|
year: nil,
|
||||||
|
content_rating: "",
|
||||||
|
state: "",
|
||||||
|
created_at: "",
|
||||||
|
updated_at: "",
|
||||||
|
volumes: []
|
||||||
|
)
|
||||||
|
do {
|
||||||
|
let regex = try NSRegularExpression(pattern: pattern)
|
||||||
|
let dir = path.appendingPathComponent("images")
|
||||||
|
|
||||||
|
let fileURLs = try fileManager.contentsOfDirectory(
|
||||||
|
at: dir,
|
||||||
|
includingPropertiesForKeys: [.isRegularFileKey],
|
||||||
|
options: [.skipsHiddenFiles]
|
||||||
|
)
|
||||||
|
let fileNames = fileURLs.map { $0.lastPathComponent }.sorted()
|
||||||
|
for fileName in fileNames {
|
||||||
|
let range = NSRange(fileName.startIndex..., in: fileName)
|
||||||
|
if let match = regex.firstMatch(in: fileName, range: range) {
|
||||||
|
let chapterStr = (fileName as NSString).substring(with: match.range(at: 1))
|
||||||
|
let chapterParts = chapterStr.split(separator: "x", maxSplits: 1)
|
||||||
|
let chapter = (
|
||||||
|
Int(chapterParts[0])!, chapterParts.count > 1 ? Int(chapterParts[1])! : nil
|
||||||
|
)
|
||||||
|
|
||||||
|
let volume = Int((fileName as NSString).substring(with: match.range(at: 2)))!
|
||||||
|
|
||||||
|
let pageStr = (fileName as NSString).substring(with: match.range(at: 3))
|
||||||
|
let pageParts = pageStr.split(separator: "-", maxSplits: 1)
|
||||||
|
let (page, doublePage) = (
|
||||||
|
Int(pageParts[0])!, pageParts.count > 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if currentVolume != nil {
|
||||||
|
if volume != currentVolume {
|
||||||
|
metadata.chapter_count += 1
|
||||||
|
assert(volume > currentVolume)
|
||||||
|
metadata.volumes.append(
|
||||||
|
VolumeMetadata.init(
|
||||||
|
volume: MetaValue.init(main: volume, bonus: nil), name: nil,
|
||||||
|
chapters: [
|
||||||
|
ChapterMetadata.init(
|
||||||
|
chapter: MetaValue.init(
|
||||||
|
main: chapter.0, bonus: chapter.1),
|
||||||
|
name: "",
|
||||||
|
images: [
|
||||||
|
ImageMetadata.init(
|
||||||
|
doublePage: doublePage, fileName: fileName,
|
||||||
|
firstPage: page)
|
||||||
|
])
|
||||||
|
]))
|
||||||
|
} else if chapter != currentChapter {
|
||||||
|
metadata.chapter_count += 1
|
||||||
|
if chapter.0 == currentChapter.0 {
|
||||||
|
assert(chapter.1! == currentChapter.1 ?? 1)
|
||||||
|
} else {
|
||||||
|
assert(chapter.0 == currentChapter.0 + 1)
|
||||||
|
}
|
||||||
|
metadata.volumes[metadata.volumes.count - 1].chapters.append(
|
||||||
|
ChapterMetadata.init(
|
||||||
|
chapter: MetaValue.init(main: chapter.0, bonus: chapter.1),
|
||||||
|
name: "",
|
||||||
|
images: [
|
||||||
|
ImageMetadata.init(
|
||||||
|
doublePage: doublePage, fileName: fileName,
|
||||||
|
firstPage: page)
|
||||||
|
]))
|
||||||
|
} else {
|
||||||
|
metadata.volumes[metadata.volumes.count - 1].chapters[
|
||||||
|
metadata.volumes[metadata.volumes.count - 1].chapters.count - 1
|
||||||
|
].images.append(
|
||||||
|
ImageMetadata.init(
|
||||||
|
doublePage: doublePage, fileName: fileName, firstPage: page)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metadata.chapter_count += 1
|
||||||
|
metadata.volumes.append(
|
||||||
|
VolumeMetadata.init(
|
||||||
|
volume: MetaValue.init(main: volume, bonus: nil), name: nil,
|
||||||
|
chapters: [
|
||||||
|
ChapterMetadata.init(
|
||||||
|
chapter: MetaValue.init(main: chapter.0, bonus: chapter.1),
|
||||||
|
name: "",
|
||||||
|
images: [
|
||||||
|
ImageMetadata.init(
|
||||||
|
doublePage: doublePage, fileName: fileName,
|
||||||
|
firstPage: page)
|
||||||
|
])
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
currentVolume = volume
|
||||||
|
currentChapter = chapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("failed reading image file names")
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
||||||
|
encoder.keyEncodingStrategy = .useDefaultKeys
|
||||||
|
try encoder.encode(
|
||||||
|
metadata
|
||||||
|
).write(
|
||||||
|
to: path.appendingPathComponent(outFileName))
|
||||||
|
} catch {
|
||||||
|
print("failed to save metadata")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func saveLocalState() {
|
func saveLocalState() {
|
||||||
let color = readerView.backgroundColor ?? .black
|
let color = readerView.backgroundColor ?? .black
|
||||||
let screenSize = UIScreen.main.bounds.size
|
let screenSize = UIScreen.main.bounds.size
|
||||||
@@ -262,9 +429,13 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
let progress: ReadProgress =
|
let progress: ReadProgress =
|
||||||
switch mode {
|
switch mode {
|
||||||
case .leftToRight:
|
case .leftToRight:
|
||||||
ReadProgress.leftToRight(chapter: currentChapterIndex, page: currentPage)
|
ReadProgress.leftToRight(
|
||||||
|
volumeIndex: progress.v, chapterIndex: progress.c,
|
||||||
|
imageIndex: progress.i)
|
||||||
case .rightToLeft:
|
case .rightToLeft:
|
||||||
ReadProgress.rightToLeft(chapter: currentChapterIndex, page: currentPage)
|
ReadProgress.rightToLeft(
|
||||||
|
volumeIndex: progress.v, chapterIndex: progress.c,
|
||||||
|
imageIndex: progress.i)
|
||||||
case .scroll: ReadProgress.scroll(scrollOffset)
|
case .scroll: ReadProgress.scroll(scrollOffset)
|
||||||
}
|
}
|
||||||
queue.async {
|
queue.async {
|
||||||
@@ -289,13 +460,15 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
).utf8)
|
).utf8)
|
||||||
let local = try JSONDecoder().decode(LocalState.self, from: json)
|
let local = try JSONDecoder().decode(LocalState.self, from: json)
|
||||||
switch local.progress {
|
switch local.progress {
|
||||||
case .leftToRight(let chapter, let page):
|
case .leftToRight(let volumeIndex, let chapterIndex, let imageIndex):
|
||||||
currentChapterIndex = chapter
|
progress.v = volumeIndex
|
||||||
currentPage = page
|
progress.c = chapterIndex
|
||||||
|
progress.i = imageIndex
|
||||||
mode = .leftToRight
|
mode = .leftToRight
|
||||||
case .rightToLeft(let chapter, let page):
|
case .rightToLeft(let volumeIndex, let chapterIndex, let imageIndex):
|
||||||
currentChapterIndex = chapter
|
progress.v = volumeIndex
|
||||||
currentPage = page
|
progress.c = chapterIndex
|
||||||
|
progress.i = imageIndex
|
||||||
mode = .rightToLeft
|
mode = .rightToLeft
|
||||||
case .scroll(let point):
|
case .scroll(let point):
|
||||||
if scrollPos == nil {
|
if scrollPos == nil {
|
||||||
@@ -306,17 +479,22 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
scrollOffset.y *= (screenSize.width / screenSize.height)
|
scrollOffset.y *= (screenSize.width / screenSize.height)
|
||||||
}
|
}
|
||||||
if let indexPath = scrollingCollectionView.indexPathForItem(at: scrollOffset) {
|
if let indexPath = scrollingCollectionView.indexPathForItem(at: scrollOffset) {
|
||||||
let (chapter, page) = chapterAndPages[indexPath.item]
|
var theProgress = ProgressIndices(v: 0, c: 0, i: 0)
|
||||||
currentChapterIndex = chapter
|
for _ in 0...indexPath.item {
|
||||||
currentPage = page
|
theProgress = getProgressIndicesFromTurn(
|
||||||
|
turn: .next, progress: theProgress)
|
||||||
|
}
|
||||||
|
progress = theProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
mode = .scroll
|
mode = .scroll
|
||||||
}
|
}
|
||||||
readerView.backgroundColor = convertStringToColor(str: local.backgroundColor)
|
readerView.backgroundColor = convertStringToColor(str: local.backgroundColor)
|
||||||
|
} catch let decodingError as DecodingError {
|
||||||
|
print(decodingError.errorDescription!)
|
||||||
} catch {
|
} catch {
|
||||||
print("failed to load local state")
|
print("Unexpected error: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,8 +518,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
|
|
||||||
if let indexPath = scrollingCollectionView.indexPathForItem(at: centerPoint) {
|
if let indexPath = scrollingCollectionView.indexPathForItem(at: centerPoint) {
|
||||||
let (chapter, page) = chapterAndPages[indexPath.item]
|
let (chapter, page) = chapterAndPages[indexPath.item]
|
||||||
currentChapterIndex = chapter
|
progress.c = chapter
|
||||||
currentPage = page
|
progress.i = page
|
||||||
}
|
}
|
||||||
if scrollingCollectionView.isHidden == false {
|
if scrollingCollectionView.isHidden == false {
|
||||||
saveLocalState()
|
saveLocalState()
|
||||||
@@ -378,6 +556,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
setNeedsStatusBarAppearanceUpdate()
|
setNeedsStatusBarAppearanceUpdate()
|
||||||
if let path = getPathFromComicName(name: name) {
|
if let path = getPathFromComicName(name: name) {
|
||||||
currentPath = path
|
currentPath = path
|
||||||
|
|
||||||
metadata = getMetadata(path: path)!
|
metadata = getMetadata(path: path)!
|
||||||
|
|
||||||
globalState.comicName = metadata.title
|
globalState.comicName = metadata.title
|
||||||
@@ -387,10 +566,12 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
scrollingCollectionView.isHidden = true
|
scrollingCollectionView.isHidden = true
|
||||||
leftView.isHidden = false
|
leftView.isHidden = false
|
||||||
rightView.isHidden = false
|
rightView.isHidden = false
|
||||||
setImages(path: path, metadata: metadata)
|
setImages(path: path)
|
||||||
} else {
|
} else {
|
||||||
setupScrolling()
|
setupScrolling()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
print("path not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,40 +579,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
leftView.isHidden = true
|
leftView.isHidden = true
|
||||||
rightView.isHidden = true
|
rightView.isHidden = true
|
||||||
scrollingCollectionView.isHidden = false
|
scrollingCollectionView.isHidden = false
|
||||||
pagesAvailable = countFiles()
|
// TODO: Fix scrolling again
|
||||||
var index = 0
|
|
||||||
var (chapter, page) = (1, 1)
|
|
||||||
chapterAndPages = Array(repeating: (0, 0), count: pagesAvailable + 1)
|
|
||||||
chapterAndPages[index] = (chapter, page)
|
|
||||||
while index < pagesAvailable {
|
|
||||||
index += 1
|
|
||||||
(chapter, page) = getChapterAndPageFromTurn(
|
|
||||||
chapter: chapter, page: page, turn: .next)
|
|
||||||
chapterAndPages[index] = (chapter, page)
|
|
||||||
}
|
|
||||||
|
|
||||||
sizeList = Array(repeating: CGSize(), count: pagesAvailable)
|
|
||||||
let binPath = currentPath.appendingPathComponent("size.bin")
|
|
||||||
let decoder = PropertyListDecoder()
|
|
||||||
do {
|
|
||||||
let data = try Data(contentsOf: binPath)
|
|
||||||
sizeList = try decoder.decode([CGSize].self, from: data)
|
|
||||||
} catch {
|
|
||||||
print("Failed to read data:", error)
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollingCollectionView.reloadData()
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let encoder = PropertyListEncoder()
|
|
||||||
encoder.outputFormat = .binary
|
|
||||||
do {
|
|
||||||
let data = try encoder.encode(self.sizeList)
|
|
||||||
try data.write(to: binPath)
|
|
||||||
} catch {
|
|
||||||
print("Encoding or writing failed:", error)
|
|
||||||
}
|
|
||||||
self.updateInfo()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func countFiles() -> Int {
|
func countFiles() -> Int {
|
||||||
@@ -752,11 +900,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
setupScrolling()
|
setupScrolling()
|
||||||
} else {
|
} else {
|
||||||
imageLoader.loadImage(
|
imageLoader.loadImage(
|
||||||
at: getImagePath(
|
at: getImagePath(progress: progress),
|
||||||
chapter: metadata.chapters[currentChapterIndex].chapter,
|
|
||||||
volume: metadata.chapters[currentChapterIndex].volume,
|
|
||||||
page: currentPage,
|
|
||||||
path: currentPath),
|
|
||||||
scaling: .scaleAspectFit
|
scaling: .scaleAspectFit
|
||||||
) { [weak self] image in
|
) { [weak self] image in
|
||||||
self?.imageView.image = image
|
self?.imageView.image = image
|
||||||
@@ -887,15 +1031,14 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
|
|
||||||
func changeImage(turn: PageTurn) {
|
func changeImage(turn: PageTurn) {
|
||||||
let scaling = UIView.ContentMode.scaleAspectFit
|
let scaling = UIView.ContentMode.scaleAspectFit
|
||||||
var (chapter, page) = getChapterAndPageFromTurn(
|
var newProgress = progress
|
||||||
chapter: currentChapterIndex, page: currentPage, turn: turn)
|
newProgress = getProgressIndicesFromTurn(turn: turn, progress: newProgress)
|
||||||
if (chapter, page) == (currentChapterIndex, currentPage) { return }
|
if newProgress.v == progress.v && newProgress.c == progress.c && newProgress.i == progress.i
|
||||||
var vol = metadata.chapters[chapter].volume
|
|
||||||
currentPage = page
|
|
||||||
currentChapterIndex = chapter
|
|
||||||
if let path = getImagePath(
|
|
||||||
chapter: metadata.chapters[chapter].chapter, volume: vol, page: page, path: currentPath)
|
|
||||||
{
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
progress = newProgress
|
||||||
|
if let path = getImagePath(progress: progress) {
|
||||||
imageLoader.loadImage(at: path, scaling: scaling) { [weak self] image in
|
imageLoader.loadImage(at: path, scaling: scaling) { [weak self] image in
|
||||||
self?.imageView.image = image
|
self?.imageView.image = image
|
||||||
self?.updateInfo()
|
self?.updateInfo()
|
||||||
@@ -905,13 +1048,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0...preloadCount {
|
for _ in 0...preloadCount {
|
||||||
(chapter, page) = getChapterAndPageFromTurn(
|
newProgress = getProgressIndicesFromTurn(turn: .next, progress: newProgress)
|
||||||
chapter: chapter, page: page, turn: .next)
|
if let path = getImagePath(progress: newProgress) {
|
||||||
vol = metadata.chapters[chapter].volume
|
|
||||||
if let path = getImagePath(
|
|
||||||
chapter: metadata.chapters[chapter].chapter, volume: vol, page: page,
|
|
||||||
path: currentPath)
|
|
||||||
{
|
|
||||||
imageLoader.preloadImage(at: path, scaling: scaling)
|
imageLoader.preloadImage(at: path, scaling: scaling)
|
||||||
} else {
|
} else {
|
||||||
print("could not preload image")
|
print("could not preload image")
|
||||||
@@ -921,35 +1059,64 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
saveLocalState()
|
saveLocalState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func metaValueToString(m: MetaValue) -> String {
|
||||||
|
if let bonus = m.bonus {
|
||||||
|
String(m.main) + "." + String(bonus)
|
||||||
|
} else {
|
||||||
|
String(m.main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateInfo() {
|
func updateInfo() {
|
||||||
if metadata == nil { return }
|
if metadata == nil { return }
|
||||||
if currentChapterIndex == nil || currentPage == nil { return }
|
|
||||||
|
|
||||||
var text = "\(metadata.title)\n"
|
var text = "\(metadata.title)\n"
|
||||||
|
|
||||||
if let chapterTitle = metadata.chapters[currentChapterIndex].title {
|
if let chapterTitle = metadata.volumes[progress.v].chapters[progress.c].name {
|
||||||
text +=
|
text +=
|
||||||
"\(chapterTitle)\n"
|
"\(chapterTitle)\n"
|
||||||
}
|
}
|
||||||
let chapterValue = metadata.chapters[currentChapterIndex].chapter
|
let chapterValue = metadata.volumes[progress.v].chapters[progress.c].chapter
|
||||||
let chapterString =
|
let chapterString = metaValueToString(m: chapterValue)
|
||||||
chapterValue.truncatingRemainder(dividingBy: 1) == 0
|
|
||||||
? String(Int(chapterValue))
|
let lastChapterValue = metadata.last_chapter
|
||||||
: String(chapterValue)
|
let lastChapterString = metaValueToString(m: lastChapterValue)
|
||||||
|
|
||||||
|
let volumeValue = metadata.volumes[progress.v].volume
|
||||||
|
let volumeString = metaValueToString(m: volumeValue)
|
||||||
|
|
||||||
|
let lastVolumeValue = metadata.last_volume
|
||||||
|
let lastVolumeString = metaValueToString(m: lastVolumeValue)
|
||||||
|
|
||||||
|
let lastVolumeIndex = metadata.volumes.count - 1
|
||||||
|
let lastChapterIndex = metadata.volumes[lastVolumeIndex].chapters.count - 1
|
||||||
|
let lastImageIndex =
|
||||||
|
metadata.volumes[lastVolumeIndex].chapters[lastChapterIndex].images.count - 1
|
||||||
|
|
||||||
|
let lastImage = metadata.volumes[lastVolumeIndex].chapters[lastChapterIndex].images[
|
||||||
|
lastImageIndex]
|
||||||
|
let lastPageString =
|
||||||
|
if lastImage.doublePage {
|
||||||
|
String(lastImage.firstPage + 1)
|
||||||
|
} else {
|
||||||
|
String(lastImage.firstPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentImage = metadata.volumes[progress.v].chapters[progress.c].images[progress.i]
|
||||||
|
let pageString =
|
||||||
|
if currentImage.doublePage {
|
||||||
|
String(currentImage.firstPage) + "-" + String(currentImage.firstPage + 1)
|
||||||
|
} else {
|
||||||
|
String(currentImage.firstPage)
|
||||||
|
}
|
||||||
|
|
||||||
text +=
|
text +=
|
||||||
"""
|
"""
|
||||||
Volume \(Int(metadata.chapters[currentChapterIndex].volume)) of \(Int(metadata.last_volume))
|
Volume \(volumeString) of \(lastVolumeString)
|
||||||
Chapter \(chapterString) of \(Int(metadata.last_chapter))
|
Chapter \(chapterString) of \(lastChapterString)
|
||||||
Page \(currentPage!) of \(metadata.chapters[currentChapterIndex].pages)
|
Page \(pageString) of \(lastPageString)
|
||||||
"""
|
"""
|
||||||
if let size = imageLoader.size[
|
if let size = imageLoader.size[getImagePath(progress: progress).path] {
|
||||||
getImagePath(
|
|
||||||
chapter: metadata.chapters[currentChapterIndex].chapter,
|
|
||||||
volume: metadata.chapters[currentChapterIndex].volume,
|
|
||||||
page: currentPage,
|
|
||||||
path: currentPath
|
|
||||||
).path]
|
|
||||||
{
|
|
||||||
text += "\nImage size: \(Int(size.width))x\(Int(size.height))"
|
text += "\nImage size: \(Int(size.width))x\(Int(size.height))"
|
||||||
}
|
}
|
||||||
info.text = text
|
info.text = text
|
||||||
@@ -968,43 +1135,47 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImagePath(chapter: Float, volume: Float, page: Int, path: URL) -> URL! {
|
func getImagePath(progress: ProgressIndices) -> URL! {
|
||||||
let modernPath = path.appendingPathComponent(String(format: "volume_%04d", Int(volume)))
|
let (v, c, i) = (progress.v, progress.c, progress.i)
|
||||||
.appendingPathComponent(
|
let modernPath = currentPath.appendingPathComponent("images").appendingPathComponent(
|
||||||
String(format: "chapter_\(formatNumber(chapter))_page_%04d.png", page))
|
metadata.volumes[v].chapters[c].images[i].fileName)
|
||||||
print("trying to get path \(modernPath)")
|
// print("trying to get path \(modernPath)")
|
||||||
// if fileManager.fileExists(atPath: modernPath.path) {
|
// if fileManager.fileExists(atPath: modernPath.path) {
|
||||||
return modernPath
|
return modernPath
|
||||||
// }
|
// }
|
||||||
// return nil
|
// return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChapterAndPageFromTurn(chapter: Int, page: Int, turn: PageTurn) -> (Int, Int) {
|
func getProgressIndicesFromTurn(turn: PageTurn, progress: ProgressIndices) -> ProgressIndices {
|
||||||
|
let (v, c, i) = (progress.v, progress.c, progress.i)
|
||||||
switch turn {
|
switch turn {
|
||||||
case .next:
|
case .next:
|
||||||
if metadata.chapters.count >= chapter + 1 {
|
if metadata.volumes[v].chapters[c].images.count > i + 1 {
|
||||||
if page + 1 > metadata.chapters[chapter].pages {
|
return ProgressIndices.init(v: v, c: c, i: i + 1)
|
||||||
return (chapter + 1, 1)
|
}
|
||||||
} else {
|
if metadata.volumes[v].chapters.count > c + 1 {
|
||||||
return (chapter, page + 1)
|
return ProgressIndices.init(v: v, c: c + 1, i: 0)
|
||||||
}
|
}
|
||||||
} else {
|
if metadata.volumes.count > v + 1 {
|
||||||
return (chapter, min(metadata.chapters[chapter].pages, page + 1))
|
return ProgressIndices.init(v: v + 1, c: 0, i: 0)
|
||||||
}
|
}
|
||||||
case .previous:
|
case .previous:
|
||||||
if page < 2 {
|
if i > 0 {
|
||||||
if chapter > 0 {
|
return ProgressIndices.init(v: v, c: c, i: i - 1)
|
||||||
return (
|
}
|
||||||
chapter - 1,
|
if c > 0 {
|
||||||
metadata.chapters[chapter - 1].pages
|
return ProgressIndices.init(
|
||||||
)
|
v: v, c: c - 1, i: metadata.volumes[v].chapters[c - 1].images.count - 1)
|
||||||
} else {
|
}
|
||||||
return (chapter, page)
|
if v > 0 {
|
||||||
}
|
return ProgressIndices.init(
|
||||||
} else {
|
v: v - 1, c: metadata.volumes[v - 1].chapters.count - 1,
|
||||||
return (chapter, max(1, page - 1))
|
i: metadata.volumes[v - 1].chapters[
|
||||||
|
metadata.volumes[v - 1].chapters.count - 1
|
||||||
|
].images.count - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ProgressIndices.init(v: v, c: c, i: i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMetadata(path: URL) -> Metadata? {
|
func getMetadata(path: URL) -> Metadata? {
|
||||||
@@ -1015,81 +1186,32 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
let metadata = try JSONDecoder().decode(Metadata.self, from: json)
|
let metadata = try JSONDecoder().decode(Metadata.self, from: json)
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
} catch let decodingError as DecodingError {
|
||||||
|
print(decodingError.errorDescription!)
|
||||||
} catch {
|
} catch {
|
||||||
print("Error reading stuff")
|
print("Unexpected error: \(error)")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setImages(path: URL, metadata: Metadata) {
|
func setImages(path: URL) {
|
||||||
let scaling: UIView.ContentMode =
|
let scaling: UIView.ContentMode =
|
||||||
if mode == .scroll {
|
if mode == .scroll {
|
||||||
.scaleAspectFill
|
.scaleAspectFill
|
||||||
} else { .scaleAspectFit }
|
} else { .scaleAspectFit }
|
||||||
var directories: [URL] = []
|
imageLoader.loadImage(
|
||||||
if currentPage != nil && currentChapterIndex != nil {
|
at:
|
||||||
var vol = metadata.chapters[currentChapterIndex].volume
|
getImagePath(progress: progress),
|
||||||
imageLoader.loadImage(
|
scaling: scaling
|
||||||
at: getImagePath(
|
) { [weak self] image in
|
||||||
chapter: metadata.chapters[currentChapterIndex].chapter, volume: vol,
|
self?.imageView.image = image
|
||||||
page: currentPage, path: currentPath
|
self?.updateInfo()
|
||||||
),
|
|
||||||
scaling: scaling
|
|
||||||
) { [weak self] image in
|
|
||||||
self?.imageView.image = image
|
|
||||||
self?.updateInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (chapter, page) = getChapterAndPageFromTurn(
|
|
||||||
chapter: currentChapterIndex, page: currentPage, turn: .next)
|
|
||||||
for _ in 0...preloadCount {
|
|
||||||
vol = metadata.chapters[chapter].volume
|
|
||||||
if let path = getImagePath(
|
|
||||||
chapter: metadata.chapters[chapter].chapter, volume: vol, page: page,
|
|
||||||
path: currentPath)
|
|
||||||
{
|
|
||||||
imageLoader.preloadImage(at: path, scaling: scaling)
|
|
||||||
} else {
|
|
||||||
print("could not preload image")
|
|
||||||
}
|
|
||||||
(chapter, page) = getChapterAndPageFromTurn(
|
|
||||||
chapter: chapter, page: page, turn: .next)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
var newProgress = progress
|
||||||
let contents = try fileManager.contentsOfDirectory(
|
for _ in 0...preloadCount {
|
||||||
at: path,
|
newProgress = getProgressIndicesFromTurn(turn: .next, progress: newProgress)
|
||||||
includingPropertiesForKeys: [.isRegularFileKey],
|
imageLoader.preloadImage(at: getImagePath(progress: newProgress), scaling: scaling)
|
||||||
options: [.skipsHiddenFiles]
|
|
||||||
)
|
|
||||||
|
|
||||||
directories = contents.filter { url in
|
|
||||||
var isDirectory: ObjCBool = false
|
|
||||||
return fileManager.fileExists(atPath: url.path, isDirectory: &isDirectory)
|
|
||||||
&& isDirectory.boolValue
|
|
||||||
}.sorted { $0.path < $1.path }
|
|
||||||
let dir: URL = directories[0]
|
|
||||||
let ps: [String] = try fileManager.contentsOfDirectory(atPath: dir.path).sorted()
|
|
||||||
let current = dir.appendingPathComponent(ps[0])
|
|
||||||
imageView.image = UIImage(contentsOfFile: current.path)
|
|
||||||
|
|
||||||
let path_meta = current.lastPathComponent.components(separatedBy: "_")
|
|
||||||
assert(path_meta[0] == "chapter")
|
|
||||||
currentChapterIndex = Int(path_meta[1])! - 1
|
|
||||||
if path_meta[2] == "page" {
|
|
||||||
currentPage = Int(path_meta[3].components(separatedBy: ".")[0])
|
|
||||||
if currentPage == nil {
|
|
||||||
print("unable to set currentPage")
|
|
||||||
assert(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
saveLocalState()
|
|
||||||
updateInfo()
|
|
||||||
} catch {
|
|
||||||
print("failed to set images")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,11 +1230,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
super.viewWillTransition(to: size, with: coordinator)
|
super.viewWillTransition(to: size, with: coordinator)
|
||||||
if mode != .scroll {
|
if mode != .scroll {
|
||||||
imageLoader.loadImage(
|
imageLoader.loadImage(
|
||||||
at: getImagePath(
|
at: getImagePath(progress: progress),
|
||||||
chapter: metadata.chapters[currentChapterIndex].chapter,
|
|
||||||
volume: metadata.chapters[currentChapterIndex].volume,
|
|
||||||
page: currentPage,
|
|
||||||
path: currentPath),
|
|
||||||
scaling: .scaleAspectFit
|
scaling: .scaleAspectFit
|
||||||
) { image in
|
) { image in
|
||||||
self.imageView.image = image
|
self.imageView.image = image
|
||||||
@@ -1175,7 +1293,7 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
|
|||||||
cell.imageView.image = comics[indexPath.item].cover
|
cell.imageView.image = comics[indexPath.item].cover
|
||||||
return cell
|
return cell
|
||||||
} else if collectionView == scrollingCollectionView {
|
} else if collectionView == scrollingCollectionView {
|
||||||
let scaling = UIView.ContentMode.scaleAspectFill
|
// let scaling = UIView.ContentMode.scaleAspectFill
|
||||||
let cell =
|
let cell =
|
||||||
collectionView.dequeueReusableCell(
|
collectionView.dequeueReusableCell(
|
||||||
withReuseIdentifier: "ScrollingImageCell", for: indexPath)
|
withReuseIdentifier: "ScrollingImageCell", for: indexPath)
|
||||||
@@ -1183,29 +1301,7 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
|
|||||||
if metadata == nil {
|
if metadata == nil {
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
var (chapterIndex, page) = chapterAndPages[indexPath.item]
|
// TODO: Fix scrolling again
|
||||||
if let url = getImagePath(
|
|
||||||
chapter: metadata.chapters[chapterIndex].chapter,
|
|
||||||
volume: metadata.chapters[chapterIndex].volume,
|
|
||||||
page: page, path: currentPath)
|
|
||||||
{
|
|
||||||
imageLoader.loadImage(at: url, scaling: scaling) { image in
|
|
||||||
cell.imageView.image = image
|
|
||||||
}
|
|
||||||
for _ in 0...preloadCount {
|
|
||||||
(chapterIndex, page) = getChapterAndPageFromTurn(
|
|
||||||
chapter: chapterIndex, page: page, turn: .next)
|
|
||||||
if let url = getImagePath(
|
|
||||||
chapter: metadata.chapters[chapterIndex].chapter,
|
|
||||||
volume: metadata.chapters[chapterIndex].volume,
|
|
||||||
page: page, path: currentPath)
|
|
||||||
{
|
|
||||||
imageLoader.preloadImage(at: url, scaling: scaling)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cell
|
return cell
|
||||||
} else {
|
} else {
|
||||||
assert(false)
|
assert(false)
|
||||||
@@ -1233,28 +1329,7 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
|
|||||||
if metadata == nil {
|
if metadata == nil {
|
||||||
return CGSize()
|
return CGSize()
|
||||||
}
|
}
|
||||||
if sizeList[indexPath.item] != CGSize(width: 0, height: 0) {
|
// TODO: Fix scrolling again
|
||||||
return CGSize(
|
|
||||||
width: readerView.bounds.width,
|
|
||||||
height: sizeList[indexPath.item].height * readerView.bounds.width
|
|
||||||
/ sizeList[indexPath.item].width)
|
|
||||||
}
|
|
||||||
let (chapter, page) = chapterAndPages[indexPath.item]
|
|
||||||
if let imagePath = getImagePath(
|
|
||||||
chapter: metadata.chapters[chapter].chapter,
|
|
||||||
volume: metadata.chapters[chapter].volume,
|
|
||||||
page: page, path: currentPath),
|
|
||||||
let imageSource = CGImageSourceCreateWithURL(imagePath as CFURL, nil),
|
|
||||||
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)
|
|
||||||
as? [CFString: Any],
|
|
||||||
let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat,
|
|
||||||
let width = imageProperties[kCGImagePropertyPixelWidth] as? CGFloat
|
|
||||||
{
|
|
||||||
sizeList[indexPath.item] = CGSize(width: width, height: height)
|
|
||||||
return CGSize(
|
|
||||||
width: readerView.bounds.width, height: height * readerView.bounds.width / width
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return CGSize()
|
return CGSize()
|
||||||
} else {
|
} else {
|
||||||
assert(false)
|
assert(false)
|
||||||
|
|||||||
Reference in New Issue
Block a user