Compare commits

..

8 Commits

Author SHA1 Message Date
Alex Ling
5645f272df When an invalid zip file is found, instead of ignoring it silently, log
a warning message
2020-02-28 18:08:56 +00:00
Alex Ling
dc3bbd10d6 Close zip file after listing entries to prevent leaking 2020-02-28 17:55:45 +00:00
Alex Ling
c89c74c71b Bump version to 0.1.2 2020-02-28 17:53:35 +00:00
Alex Ling
cb76a96126 Removing the logging of the library json on scan 2020-02-28 17:52:50 +00:00
Alex Ling
73b38492ba Remove console in minified JS files 2020-02-28 17:52:14 +00:00
Alex Ling
bf37c4aa10 Sorting in library and title page 2020-02-28 17:51:26 +00:00
Alex Ling
f837be0718 Ignore invalid zip files in library 2020-02-28 17:45:30 +00:00
Alex Ling
98baf63b0c Add gitter to README 2020-02-24 22:19:31 -05:00
7 changed files with 119 additions and 24 deletions

View File

@@ -1,7 +1,12 @@
# Mango
![banner](./public/img/banner-paddings.png)
# Mango
[![Gitter](https://badges.gitter.im/mango-cr/mango.svg)](https://gitter.im/mango-cr/mango?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Mango is a self-hosted manga server and reader. Its features include
- Multi-user support

View File

@@ -4,7 +4,9 @@ const minifyCss = require('gulp-minify-css');
gulp.task('minify-js', () => {
return gulp.src('public/js/*.js')
.pipe(minify())
.pipe(minify({
removeConsole: true
}))
.pipe(gulp.dest('dist/js'));
});

35
public/js/sort-items.js Normal file
View File

@@ -0,0 +1,35 @@
$(() => {
$('option#name-up').attr('selected', '');
$('#sort-select').change(() => {
const sort = $('#sort-select').find(':selected').attr('id');
const ary = sort.split('-');
const by = ary[0];
const dir = ary[1];
const items = $('.item');
items.remove();
items.sort((a, b) => {
var res;
if (by === 'name')
res = $(a).find('.uk-card-title').text() > $(b).find('.uk-card-title').text();
else if (by === 'date')
res = $(a).attr('data-mtime') > $(b).attr('data-mtime');
else {
const ap = $(a).attr('data-progress');
const bp = $(b).attr('data-progress');
if (ap === bp)
// if progress is the same, we compare by name
res = $(a).find('.uk-card-title').text() > $(b).find('.uk-card-title').text();
else
res = ap > bp;
}
if (dir === 'up')
return res;
else
return !res;
});
var html = '';
$('#item-container').append(items);
});
});

View File

@@ -16,7 +16,8 @@ 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
title_id: String, encoded_path: String, encoded_title: String,
mtime: Time
def initialize(path, @book_title, @title_id, storage)
@zip_path = path
@@ -24,14 +25,17 @@ class Entry
@title = File.basename path, File.extname path
@encoded_title = URI.encode @title
@size = (File.size path).humanize_bytes
@pages = Zip::File.new(path).entries
file = Zip::File.new path
@pages = file.entries
.select { |e|
["image/jpeg", "image/png"].includes? \
MIME.from_filename? e.filename
}
.size
file.close
@id = storage.get_id @zip_path, false
@cover_url = "/api/page/#{@title_id}/#{@id}/1"
@mtime = File.info(@zip_path).modification_time
end
def read_page(page_num)
Zip::File.open @zip_path do |file|
@@ -57,20 +61,40 @@ end
class Title
JSON.mapping dir: String, entries: Array(Entry), title: String,
id: String, encoded_title: String
id: String, encoded_title: String, mtime: Time, logger: MLogger
def initialize(dir : String, storage)
def initialize(dir : String, storage, @logger : MLogger)
@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 File.join(dir, path), @title, @id, storage
Entry.new path, @title, @id, storage
}
.select { |e| e.pages > 0 }
.sort { |a, b| a.title <=> b.title }
mtimes = [File.info(dir).modification_time]
mtimes += @entries.map{|e| e.mtime}
@mtime = mtimes.max
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
# constructing Entry
private def valid_zip(path : String)
begin
file = Zip::File.new path
file.close
return true
rescue
@logger.warn "File #{path} is corrupted or is not a valid zip "\
"archive. Ignoring it."
return false
end
end
def get_entry(eid)
@entries.find { |e| e.id == eid }
@@ -178,10 +202,9 @@ class Library
end
@titles = (Dir.entries @dir)
.select { |path| File.directory? File.join @dir, path }
.map { |path| Title.new File.join(@dir, path), @storage }
.map { |path| Title.new File.join(@dir, path), @storage, @logger }
.select { |title| !title.entries.empty? }
.sort { |a, b| a.title <=> b.title }
@logger.debug "Scan completed"
@logger.debug "Scanned library: \n#{self.to_pretty_json}"
end
end

View File

@@ -2,7 +2,7 @@ require "./server"
require "./context"
require "option_parser"
VERSION = "0.1.0"
VERSION = "0.1.2"
config_path = nil

View File

@@ -1,14 +1,28 @@
<h2 class=uk-title>Library</h2>
<p class="uk-text-meta"><%= titles.size %> titles found</p>
<div class="uk-margin">
<form class="uk-search uk-search-default">
<span uk-search-icon></span>
<input class="uk-search-input" type="search" placeholder="Search">
</form>
<div class="uk-grid-small" uk-grid>
<div class="uk-margin-bottom uk-width-3-4@s">
<form class="uk-search uk-search-default">
<span uk-search-icon></span>
<input class="uk-search-input" type="search" placeholder="Search">
</form>
</div>
<div class="uk-margin-bottom uk-width-1-4@s">
<div class="uk-form-horizontal">
<select class="uk-select" id="sort-select">
<option id="name-up">▲ Name</option>
<option id="name-down">▼ Name</option>
<option id="date-up">▲ Date Modified</option>
<option id="date-down">▼ Date Modified</option>
<option id="progress-up">▲ Progress</option>
<option id="progress-down">▼ Progress</option>
</select>
</div>
</div>
</div>
<div class="uk-child-width-1-4@m uk-child-width-1-2" uk-grid>
<div id="item-container" class="uk-child-width-1-4@m uk-child-width-1-2" uk-grid>
<%- titles.each_with_index do |t, i| -%>
<div class="item">
<div class="item" data-mtime="<%= t.mtime.to_unix %>" data-progress="<%= percentage[i] %>">
<a class="acard" href="/book/<%= t.id %>">
<div class="uk-card uk-card-default">
<div class="uk-card-media-top">
@@ -27,4 +41,5 @@
<% content_for "script" do %>
<script src="/js/search.js"></script>
<script src="/js/sort-items.js"></script>
<% end %>

View File

@@ -1,15 +1,29 @@
<div id="alert"></div>
<h2 class=uk-title><%= title.title %></h2>
<p class="uk-text-meta"><%= title.entries.size %> entries found</p>
<div class="uk-margin">
<form class="uk-search uk-search-default">
<span uk-search-icon></span>
<input class="uk-search-input" type="search" placeholder="Search">
</form>
<div class="uk-grid-small" uk-grid>
<div class="uk-margin-bottom uk-width-3-4@s">
<form class="uk-search uk-search-default">
<span uk-search-icon></span>
<input class="uk-search-input" type="search" placeholder="Search">
</form>
</div>
<div class="uk-margin-bottom uk-width-1-4@s">
<div class="uk-form-horizontal">
<select class="uk-select" id="sort-select">
<option id="name-up">▲ Name</option>
<option id="name-down">▼ Name</option>
<option id="date-up">▲ Date Modified</option>
<option id="date-down">▼ Date Modified</option>
<option id="progress-up">▲ Progress</option>
<option id="progress-down">▼ Progress</option>
</select>
</div>
</div>
</div>
<div class="uk-child-width-1-4@m uk-child-width-1-2" uk-grid>
<div id="item-container" class="uk-child-width-1-4@m uk-child-width-1-2" uk-grid>
<%- title.entries.each_with_index do |e, i| -%>
<div class="item">
<div class="item" data-mtime="<%= e.mtime.to_unix %>" data-progress="<%= percentage[i] %>">
<a class="acard">
<div class="uk-card uk-card-default" onclick="showModal(&quot;<%= e.encoded_path %>&quot;, '<%= e.pages %>', <%= (percentage[i] * 100).round(1) %>, &quot;<%= title.encoded_title %>&quot;, &quot;<%= e.encoded_title %>&quot;, '<%= e.title_id %>', '<%= e.id %>')">
<div class="uk-card-media-top">
@@ -51,4 +65,5 @@
<% content_for "script" do %>
<script src="/js/title.js"></script>
<script src="/js/search.js"></script>
<script src="/js/sort-items.js"></script>
<% end %>