diff --git a/src/library.cr b/src/library.cr
index eb567b4..7835b2c 100644
--- a/src/library.cr
+++ b/src/library.cr
@@ -15,10 +15,10 @@ struct Image
end
class Entry
- JSON.mapping zip_path: String, book_title: String, title: String,
- size: String, pages: Int32, cover_url: String, id: String,
- title_id: String, encoded_path: String, encoded_title: String,
- mtime: Time
+ property zip_path : String, book_title : String, title : String,
+ size : String, pages : Int32, cover_url : String, id : String,
+ title_id : String, encoded_path : String, encoded_title : String,
+ mtime : Time
def initialize(path, @book_title, @title_id, storage)
@zip_path = path
@@ -38,6 +38,19 @@ class Entry
@cover_url = "/api/page/#{@title_id}/#{@id}/1"
@mtime = File.info(@zip_path).modification_time
end
+
+ def to_json(json : JSON::Builder)
+ json.object do
+ {% for str in ["zip_path", "book_title", "title", "size",
+ "cover_url", "id", "title_id", "encoded_path",
+ "encoded_title"] %}
+ json.field {{str}}, @{{str.id}}
+ {% end %}
+ json.field "pages" {json.number @pages}
+ json.field "mtime" {json.number @mtime.to_unix}
+ end
+ end
+
def read_page(page_num)
Zip::File.open @zip_path do |file|
page = file.entries
@@ -63,27 +76,91 @@ class Entry
end
class Title
- JSON.mapping dir: String, entries: Array(Entry), title: String,
- id: String, encoded_title: String, mtime: Time, logger: MLogger
+ property dir : String, parent_id : String, title_ids : Array(String),
+ entries : Array(Entry), title : String, id : String,
+ encoded_title : String, mtime : Time
- def initialize(dir : String, storage, @logger : MLogger)
+ def initialize(dir : String, @parent_id, storage,
+ @logger : MLogger, @library : Library)
@dir = dir
@id = storage.get_id @dir, true
@title = File.basename dir
@encoded_title = URI.encode @title
- @entries = (Dir.entries dir)
- .select { |path| [".zip", ".cbz"].includes? File.extname path }
- .map { |path| File.join dir, path }
- .select { |path| valid_zip path }
- .map { |path|
- Entry.new path, @title, @id, storage
- }
- .select { |e| e.pages > 0 }
- .sort { |a, b| a.title <=> b.title }
+ @title_ids = [] of String
+ @entries = [] of Entry
+
+ Dir.entries(dir).each do |fn|
+ next if fn.starts_with? "."
+ path = File.join dir, fn
+ if File.directory? path
+ title = Title.new path, @id, storage, @logger, library
+ next if title.entries.size == 0 && title.titles.size == 0
+ @library.title_hash[title.id] = title
+ @title_ids << title.id
+ next
+ end
+ if [".zip", ".cbz"].includes? File.extname path
+ next if !valid_zip path
+ entry = Entry.new path, @title, @id, storage
+ @entries << entry if entry.pages > 0
+ end
+ end
+
+ @title_ids.sort! { |a,b|
+ @library.title_hash[a].title <=> @library.title_hash[b].title
+ }
+ @entries.sort! { |a,b| a.title <=> b.title }
+
mtimes = [File.info(dir).modification_time]
+ mtimes += @title_ids.map{|e| @library.title_hash[e].mtime}
mtimes += @entries.map{|e| e.mtime}
@mtime = mtimes.max
end
+
+ def to_json(json : JSON::Builder)
+ json.object do
+ {% for str in ["dir", "title", "id", "encoded_title"] %}
+ json.field {{str}}, @{{str.id}}
+ {% end %}
+ json.field "mtime" {json.number @mtime.to_unix}
+ json.field "titles" do
+ json.raw self.titles.to_json
+ end
+ json.field "entries" do
+ json.raw @entries.to_json
+ end
+ json.field "parents" do
+ json.array do
+ self.parents.each do |title|
+ json.object do
+ json.field "title", title.title
+ json.field "id", title.id
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def titles
+ @title_ids.map {|tid| @library.get_title! tid}
+ end
+
+ def parents
+ ary = [] of Title
+ tid = @parent_id
+ while !tid.empty?
+ title = @library.get_title! tid
+ ary << title
+ tid = title.parent_id
+ end
+ ary
+ end
+
+ def size
+ @entries.size + @title_ids.size
+ end
+
# When downloading from MangaDex, the zip/cbz file would not be valid
# before the download is completed. If we scan the zip file,
# Entry.new would throw, so we use this method to check before
@@ -150,10 +227,7 @@ class TitleInfo
# { user1: { entry1: 10, entry2: 0 } }
include JSON::Serializable
- @[JSON::Field(key: "comment")]
property comment = "Generated by Mango. DO NOT EDIT!"
-
- @[JSON::Field(key: "progress")]
property progress : Hash(String, Hash(String, Int32))
def initialize(title_dir)
@@ -175,13 +249,14 @@ class TitleInfo
end
class Library
- JSON.mapping dir: String, titles: Array(Title), scan_interval: Int32,
- logger: MLogger, storage: Storage
+ property dir : String, title_ids : Array(String), scan_interval : Int32,
+ logger : MLogger, storage : Storage, title_hash : Hash(String, Title)
def initialize(@dir, @scan_interval, @logger, @storage)
# explicitly initialize @titles to bypass the compiler check. it will
# be filled with actual Titles in the `scan` call below
- @titles = [] of Title
+ @title_ids = [] of String
+ @title_hash = {} of String => Title
return scan if @scan_interval < 1
spawn do
@@ -189,13 +264,27 @@ class Library
start = Time.local
scan
ms = (Time.local - start).total_milliseconds
- @logger.info "Scanned #{@titles.size} titles in #{ms}ms"
+ @logger.info "Scanned #{@title_ids.size} titles in #{ms}ms"
sleep @scan_interval * 60
end
end
end
+ def titles
+ @title_ids.map {|tid| self.get_title!(tid) }
+ end
+ def to_json(json : JSON::Builder)
+ json.object do
+ json.field "dir", @dir
+ json.field "titles" do
+ json.raw self.titles.to_json
+ end
+ end
+ end
def get_title(tid)
- @titles.find { |t| t.id == tid }
+ @title_hash[tid]?
+ end
+ def get_title!(tid)
+ @title_hash[tid]
end
def scan
unless Dir.exists? @dir
@@ -203,11 +292,18 @@ class Library
"Attempting to create it"
Dir.mkdir_p @dir
end
- @titles = (Dir.entries @dir)
- .select { |path| File.directory? File.join @dir, path }
- .map { |path| Title.new File.join(@dir, path), @storage, @logger }
- .select { |title| !title.entries.empty? }
+ @title_ids.clear
+ (Dir.entries @dir)
+ .select { |fn| !fn.starts_with? "." }
+ .map { |fn| File.join @dir, fn }
+ .select { |path| File.directory? path }
+ .map { |path| Title.new path, "", @storage, @logger, self }
+ .select { |title| !(title.entries.empty? && title.titles.empty?) }
.sort { |a, b| a.title <=> b.title }
+ .each do |title|
+ @title_hash[title.id] = title
+ @title_ids << title.id
+ end
@logger.debug "Scan completed"
end
end
diff --git a/src/routes/api.cr b/src/routes/api.cr
index c8ec60d..cefb1b6 100644
--- a/src/routes/api.cr
+++ b/src/routes/api.cr
@@ -26,7 +26,7 @@ class APIRouter < Router
end
end
- get "/api/book/:title" do |env|
+ get "/api/book/:tid" do |env|
begin
tid = env.params.url["tid"]
title = @context.library.get_title tid
diff --git a/src/routes/main.cr b/src/routes/main.cr
index a2c3f48..92408e2 100644
--- a/src/routes/main.cr
+++ b/src/routes/main.cr
@@ -47,6 +47,8 @@ class MainRouter < Router
username = get_username env
percentage = title.entries.map { |e|
title.load_percetage username, e.title }
+ titles_percentage = title.titles.map { |t|
+ title.load_percetage username, t.title }
layout "title"
rescue e
@context.error e
diff --git a/src/views/index.ecr b/src/views/index.ecr
index 1aee880..bc3c591 100644
--- a/src/views/index.ecr
+++ b/src/views/index.ecr
@@ -26,12 +26,18 @@
<%= t.entries.size %> entries <%= t.size %> entries
+ <%- else -%>
+
+ <%- end -%>
<%= t.title %>
-