Skip to content
This repository was archived by the owner on Mar 23, 2025. It is now read-only.

Commit 14bf4da

Browse files
committed
Merge branch 'dev'
2 parents 3039031 + a72dfce commit 14bf4da

File tree

17 files changed

+265
-105
lines changed

17 files changed

+265
-105
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
steps:
1818
- uses: actions/checkout@v2
1919
- name: Install dependencies
20-
run: apk add --no-cache yarn yaml sqlite-static
20+
run: apk add --no-cache yarn yaml sqlite-static libarchive-dev libarchive-static acl-static expat-static zstd-static lz4-static bzip2-static
2121
- name: Build
2222
run: make static
2323
- name: Linter

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ WORKDIR /Mango
44

55
COPY . .
66
COPY package*.json .
7-
RUN apk add --no-cache yarn yaml sqlite-static \
7+
RUN apk add --no-cache yarn yaml sqlite-static libarchive-dev libarchive-static acl-static expat-static zstd-static lz4-static bzip2-static \
88
&& make static
99

1010
FROM library/alpine

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ static: uglify | libs
1414
crystal build src/mango.cr --release --progress --static
1515

1616
libs:
17-
shards install
17+
shards install --production
1818

1919
run:
2020
crystal run src/mango.cr --error-trace

README.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Mango is a self-hosted manga server and reader. Its features include
1111

1212
- Multi-user support
1313
- Dark/light mode switch
14-
- Supports both `.zip` and `.cbz` formats
14+
- Supported formats: `.cbz`, `.zip`, `.cbr` and `.rar`
1515
- Supports nested folders in library
1616
- Automatically stores reading progress
1717
- Built-in [MangaDex](https://mangadex.org/) downloader
@@ -39,7 +39,7 @@ The official docker images are available on [Dockerhub](https://hub.docker.com/r
3939

4040
### Build from source
4141

42-
1. Make sure you have `crystal`, `shards` and `yarn` installed. You might also need to install the development headers for `libsqlite3` and `libyaml`.
42+
1. Make sure you have `crystal`, `shards` and `yarn` installed. You might also need to install the development headers of some libraries. Please see the [Dockerfile](https://github.com/hkalexling/Mango/blob/master/Dockerfile) for the full list of dependencies
4343
2. Clone the repository
4444
3. `make && sudo make install`
4545
4. Start Mango by running the command `mango`
@@ -50,11 +50,21 @@ The official docker images are available on [Dockerhub](https://hub.docker.com/r
5050
### CLI
5151

5252
```
53-
Mango e-manga server/reader. Version 0.4.0
53+
Mango - Manga Server and Web Reader. Version 0.5.0
5454
55-
-v, --version Show version
56-
-h, --help Show help
57-
-c PATH, --config=PATH Path to the config file. Default is `~/.config/mango/config.yml`
55+
Usage:
56+
57+
mango [sub_command] [options]
58+
59+
Options:
60+
61+
-c PATH, --config=PATH Path to the config file [type:String]
62+
-h, --help Show this help.
63+
-v, --version Show version.
64+
65+
Sub Commands:
66+
67+
admin Run admin tools
5868
```
5969

6070
### Config
@@ -84,7 +94,7 @@ mangadex:
8494

8595
### Library Structure
8696

87-
You can organize your `.cbz/.zip` files in nested folders in the library directory. Here's an example:
97+
You can organize your archive files in nested folders in the library directory. Here's an example:
8898

8999
```
90100
.

public/js/download.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,10 @@ const buildTable = () => {
242242
Object.entries(filters).forEach(([k, v]) => {
243243
if (v === 'All') return;
244244
if (k === 'group') {
245-
chapters = chapters.filter(c => v in c.groups);
245+
chapters = chapters.filter(c => {
246+
unescaped_groups = Object.entries(c.groups).map(([g, id]) => unescapeHTML(g));
247+
return unescaped_groups.indexOf(v) >= 0;
248+
});
246249
return;
247250
}
248251
if (k === 'lang') {
@@ -297,3 +300,9 @@ const buildTable = () => {
297300
});
298301
$('#selection-controls').removeAttr('hidden');
299302
};
303+
304+
const unescapeHTML = (str) => {
305+
var elt = document.createElement("span");
306+
elt.innerHTML = str;
307+
return elt.innerText;
308+
};

shard.lock

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@ version: 1.0
22
shards:
33
ameba:
44
github: crystal-ameba/ameba
5-
version: 0.12.0
5+
version: 0.12.1
6+
7+
archive:
8+
github: hkalexling/archive.cr
9+
version: 0.2.0
610

711
baked_file_system:
812
github: schovi/baked_file_system
913
version: 0.9.8
1014

15+
clim:
16+
github: at-grandpa/clim
17+
version: 0.12.0
18+
1119
db:
1220
github: crystal-lang/crystal-db
1321
version: 0.9.0

shard.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: mango
2-
version: 0.4.0
2+
version: 0.5.0
33

44
authors:
55
- Alex Ling <hkalexling@gmail.com>
@@ -19,7 +19,9 @@ dependencies:
1919
github: crystal-lang/crystal-sqlite3
2020
baked_file_system:
2121
github: schovi/baked_file_system
22-
23-
development_dependencies:
22+
archive:
23+
github: hkalexling/archive.cr
2424
ameba:
2525
github: crystal-ameba/ameba
26+
clim:
27+
github: at-grandpa/clim

spec/spec_helper.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ end
4545
def with_storage
4646
with_default_config do
4747
temp_db = get_tempfile "mango-test-db"
48-
storage = Storage.new temp_db.path
48+
storage = Storage.new temp_db.path, false
4949
clear = yield storage, temp_db.path
5050
if clear == true
5151
temp_db.delete

src/archive.cr

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
require "zip"
2+
require "archive"
3+
4+
# A unified class to handle all supported archive formats. It uses the ::Zip
5+
# module in crystal standard library if the target file is a zip archive.
6+
# Otherwise it uses `archive.cr`.
7+
class ArchiveFile
8+
def initialize(@filename : String)
9+
if [".cbz", ".zip"].includes? File.extname filename
10+
@archive_file = Zip::File.new filename
11+
else
12+
@archive_file = Archive::File.new filename
13+
end
14+
end
15+
16+
def self.open(filename : String, &)
17+
s = self.new filename
18+
yield s
19+
s.close
20+
end
21+
22+
def close
23+
if @archive_file.is_a? Zip::File
24+
@archive_file.as(Zip::File).close
25+
end
26+
end
27+
28+
# Lists all file entries
29+
def entries
30+
ary = [] of Zip::File::Entry | Archive::Entry
31+
@archive_file.entries.map do |e|
32+
if (e.is_a? Zip::File::Entry && e.file?) ||
33+
(e.is_a? Archive::Entry && e.info.file?)
34+
ary.push e
35+
end
36+
end
37+
ary
38+
end
39+
40+
def read_entry(e : Zip::File::Entry | Archive::Entry) : Bytes?
41+
if e.is_a? Zip::File::Entry
42+
data = nil
43+
e.open do |io|
44+
slice = Bytes.new e.uncompressed_size
45+
bytes_read = io.read_fully? slice
46+
data = slice if bytes_read
47+
end
48+
data
49+
else
50+
e.read
51+
end
52+
end
53+
54+
def check
55+
if @archive_file.is_a? Archive::File
56+
@archive_file.as(Archive::File).check
57+
end
58+
end
59+
end

src/handlers/auth_handler.cr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ class AuthHandler < Kemal::Handler
99
def call(env)
1010
return call_next(env) if request_path_startswith env, ["/login", "/logout"]
1111

12-
cookie = env.request.cookies.find { |c| c.name == "token" }
12+
cookie = env.request.cookies.find do |c|
13+
c.name == "token-#{Config.current.port}"
14+
end
1315
if cookie.nil? || !@storage.verify_token cookie.value
1416
return redirect env, "/login"
1517
end

src/library.cr

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
require "zip"
21
require "mime"
32
require "json"
43
require "uri"
54
require "./util"
5+
require "./archive"
66

77
struct Image
88
property data : Bytes
@@ -25,7 +25,7 @@ class Entry
2525
@title = File.basename path, File.extname path
2626
@encoded_title = URI.encode @title
2727
@size = (File.size path).humanize_bytes
28-
file = Zip::File.new path
28+
file = ArchiveFile.new path
2929
@pages = file.entries.count do |e|
3030
["image/jpeg", "image/png"].includes? \
3131
MIME.from_filename? e.filename
@@ -68,7 +68,8 @@ class Entry
6868
end
6969

7070
def read_page(page_num)
71-
Zip::File.open @zip_path do |file|
71+
img = nil
72+
ArchiveFile.open @zip_path do |file|
7273
page = file.entries
7374
.select { |e|
7475
["image/jpeg", "image/png"].includes? \
@@ -78,16 +79,13 @@ class Entry
7879
compare_alphanumerically a.filename, b.filename
7980
}
8081
.[page_num - 1]
81-
page.open do |io|
82-
slice = Bytes.new page.uncompressed_size
83-
bytes_read = io.read_fully? slice
84-
unless bytes_read
85-
return nil
86-
end
87-
return Image.new slice, MIME.from_filename(page.filename),
88-
page.filename, bytes_read
82+
data = file.read_entry page
83+
if data
84+
img = Image.new data, MIME.from_filename(page.filename), page.filename,
85+
data.size
8986
end
9087
end
88+
img
9189
end
9290
end
9391

@@ -115,12 +113,16 @@ class Title
115113
@title_ids << title.id
116114
next
117115
end
118-
if [".zip", ".cbz"].includes? File.extname path
119-
zip_exception = validate_zip path
120-
unless zip_exception.nil?
121-
Logger.warn "File #{path} is corrupted or is not a valid zip " \
122-
"archive. Ignoring it."
123-
Logger.debug "Zip error: #{zip_exception}"
116+
if [".zip", ".cbz", ".rar", ".cbr"].includes? File.extname path
117+
unless File.readable? path
118+
Logger.warn "File #{path} is not readable. Please make sure the " \
119+
"file permission is configured correctly."
120+
next
121+
end
122+
archive_exception = validate_archive path
123+
unless archive_exception.nil?
124+
Logger.warn "Unable to extract archive #{path}. Ignoring it. " \
125+
"Archive error: #{archive_exception}"
124126
next
125127
end
126128
entry = Entry.new path, self, @id, storage

src/mangadex/downloader.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ module MangaDex
371371
writer.close
372372
Logger.debug "cbz File created at #{zip_path}"
373373

374-
zip_exception = validate_zip zip_path
374+
zip_exception = validate_archive zip_path
375375
if !zip_exception.nil?
376376
@queue.add_message "The downloaded archive is corrupted. " \
377377
"Error: #{zip_exception}", job

0 commit comments

Comments
 (0)