mirror of
https://github.com/gen2brain/cbconvert
synced 2026-06-30 17:21:54 +02:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28ef6910e4 | |||
| 35b8bc340f | |||
| a5a45306c6 | |||
| b063367110 | |||
| 7831e487e6 | |||
| ce32d48469 | |||
| 4d1e0a44d2 | |||
| 574e364954 | |||
| 97aae5e0c7 | |||
| bbcaf0f9dd | |||
| d70de9f4db | |||
| d7be2f4d5e | |||
| 82e8a6cbac | |||
| 85818eec5f | |||
| 0c6b9fcaa8 | |||
| 55b53efcca | |||
| 4abf99a644 | |||
| ca4f13ebfe | |||
| a4c7b87dc0 | |||
| dd4c3a2dbf | |||
| 8e3f7962d1 | |||
| 1c3421028b |
@@ -4,52 +4,92 @@ CBconvert
|
|||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
CBconvert is a [Comic Book](http://en.wikipedia.org/wiki/Comic_Book_Archive_file) convert tool.
|
CBconvert is a [Comic Book](http://en.wikipedia.org/wiki/Comic_Book_Archive_file) converter.
|
||||||
|
It allows you to convert individual comics or bulk convert comics to different formats to fit your various devices.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
- reads RAR, ZIP, 7Z, GZ, BZ2, CBR, CBZ, CB7, CBT, PDF, EPUB, XPS and plain directory
|
- reads RAR, ZIP, 7Z, GZ, BZ2, CBR, CBZ, CB7, CBT, PDF, EPUB, XPS and plain directory
|
||||||
- always saves processed comic in CBZ (ZIP) format
|
- always saves processed comic in CBZ (ZIP) archive format
|
||||||
- images can be converted to JPEG, PNG or 4-Bit BMP (16 colors) format
|
- images can be converted to JPEG, PNG, GIF, TIFF or 4-Bit BMP (16 colors) file format
|
||||||
- choose resize algorithm (NearestNeighbor, Bilinear, Bicubic, MitchellNetravali, Lanczos2/3)
|
- choose resize algorithm (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
|
||||||
|
- rotate, flip, adjust brightness/contrast or grayscale images
|
||||||
- export covers from comics
|
- export covers from comics
|
||||||
- create thumbnails from covers by [freedesktop](http://www.freedesktop.org/wiki/) specification
|
- create thumbnails from covers by [freedesktop](http://www.freedesktop.org/wiki/) specification
|
||||||
|
|
||||||
Download
|
Download
|
||||||
--------
|
--------
|
||||||
|
|
||||||
- [Windows static build](https://github.com/gen2brain/cbconvert/releases/download/0.2.0/cbconvert-0.2.0.zip)
|
- [Windows binary](https://github.com/gen2brain/cbconvert/releases/download/0.4.0/cbconvert-0.4.0.zip)
|
||||||
- [Linux 64bit build](https://github.com/gen2brain/cbconvert/releases/download/0.2.0/cbconvert-0.2.0.tar.gz)
|
|
||||||
|
- [Linux 64bit binary](https://github.com/gen2brain/cbconvert/releases/download/0.4.0/cbconvert-0.4.0.tar.gz)
|
||||||
|
- [Linux 64bit static binary](https://github.com/gen2brain/cbconvert/releases/download/0.4.0/cbconvert-0.4.0-static.tar.gz)
|
||||||
|
|
||||||
Using
|
Using
|
||||||
-----
|
-----
|
||||||
|
|
||||||
usage: cbconvert [<flags>] <args>...
|
usage: cbconvert [<flags>] <command> [<args> ...]
|
||||||
|
|
||||||
Comic Book convert tool.
|
Comic Book convert tool.
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
--help Show context-sensitive help (also try --help-long and --help-man).
|
--help Show context-sensitive help (also try --help-long and --help-man).
|
||||||
--version Show application version.
|
--version Show application version.
|
||||||
-p, --png encode images to PNG instead of JPEG
|
--outdir="." Output directory
|
||||||
-b, --bmp encode images to 4-Bit BMP instead of JPEG
|
--size=0 Process only files larger then size (in MB)
|
||||||
-w, --width=0 image width
|
--recursive Process subdirectories recursively
|
||||||
-h, --height=0 image height
|
--quiet Hide console output
|
||||||
-q, --quality=75 JPEG image quality
|
|
||||||
-n, --norgb do not convert images with RGB colorspace
|
|
||||||
-i, --interpolation=1 0=NearestNeighbor, 1=Bilinear, 2=Bicubic, 3=MitchellNetravali, 4=Lanczos2, 5=Lanczos3
|
|
||||||
-s, --suffix=SUFFIX add suffix to file basename
|
|
||||||
-c, --cover extract cover
|
|
||||||
-t, --thumbnail extract cover thumbnail (freedesktop spec.)
|
|
||||||
-o, --outdir="." output directory
|
|
||||||
-m, --size=0 process only files larger then size (in MB)
|
|
||||||
-r, --recursive process subdirectories recursively
|
|
||||||
-Q, --quiet hide console output
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
<args> filename or directory
|
<args> filename or directory
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
help [<command>...]
|
||||||
|
Show help.
|
||||||
|
|
||||||
|
|
||||||
|
convert [<flags>] <args>...
|
||||||
|
Convert archive or document (default command)
|
||||||
|
|
||||||
|
--width=0 Image width
|
||||||
|
--height=0 Image height
|
||||||
|
--fit Best fit for required width and height
|
||||||
|
--quality=75 JPEG image quality
|
||||||
|
--filter=2 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
||||||
|
--png Encode images to PNG instead of JPEG
|
||||||
|
--bmp Encode images to 4-Bit BMP (16 colors) instead of JPEG
|
||||||
|
--gif Encode images to GIF instead of JPEG
|
||||||
|
--tiff Encode images to TIFF instead of JPEG
|
||||||
|
--rgb Convert images that have RGB colorspace (use --no-rgb if you only want to convert grayscale images)
|
||||||
|
--nonimage Leave non image files in archive (use --no-nonimage to remove non image files from archive)
|
||||||
|
--grayscale Convert images to grayscale (monochromatic)
|
||||||
|
--rotate=0 Rotate images, valid values are 0, 90, 180, 270
|
||||||
|
--flip="none" Flip images, valid values are none, horizontal, vertical
|
||||||
|
--brightness=0 Adjust brightness of the images, must be in range (-100, 100)
|
||||||
|
--contrast=0 Adjust contrast of the images, must be in range (-100, 100)
|
||||||
|
--suffix=SUFFIX Add suffix to file basename
|
||||||
|
|
||||||
|
cover [<flags>] <args>...
|
||||||
|
Extract cover
|
||||||
|
|
||||||
|
--width=0 Image width
|
||||||
|
--height=0 Image height
|
||||||
|
--fit Best fit for required width and height
|
||||||
|
--quality=75 JPEG image quality
|
||||||
|
--filter=2 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
||||||
|
|
||||||
|
thumbnail [<flags>] <args>...
|
||||||
|
Extract cover thumbnail (freedesktop spec.)
|
||||||
|
|
||||||
|
--width=0 Image width
|
||||||
|
--height=0 Image height
|
||||||
|
--fit Best fit for required width and height
|
||||||
|
--filter=2 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
||||||
|
|
||||||
|
[man page](https://en.wikipedia.org/wiki/Man_page) is also available:
|
||||||
|
|
||||||
|
cbconvert --help-man | man /dev/stdin
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
@@ -58,15 +98,19 @@ Rescale images to 1200px for all supported files found in directory with size la
|
|||||||
|
|
||||||
cbconvert --recursive --width 1200 --size 60 /media/comics/Thorgal/
|
cbconvert --recursive --width 1200 --size 60 /media/comics/Thorgal/
|
||||||
|
|
||||||
Convert all images in archive to 4bit BMP image and save result in ~/comics directory:
|
Convert all images in pdf to 4bit BMP image and save result in ~/comics directory:
|
||||||
|
|
||||||
cbconvert --bmp --outdir ~/comics /media/comics/Garfield/Garfield_01.cbz
|
cbconvert --bmp --outdir ~/comics /media/comics/Garfield/Garfield_01.pdf
|
||||||
|
|
||||||
[BMP](http://en.wikipedia.org/wiki/BMP_file_format) format is uncompressed, for black&white pages very good choice. Archive size can be smaller 2-3x and file will be readable by comic readers.
|
[BMP](http://en.wikipedia.org/wiki/BMP_file_format) format is very good choice for black&white pages. Archive size can be smaller 2-3x and file will be readable by comic readers.
|
||||||
|
|
||||||
Generate thumbnails by freedesktop specification in ~/.thumbnails/normal directory, Lanczos3 algorithm is used for resizing:
|
Generate thumbnails by freedesktop specification in ~/.thumbnails/normal directory with width 512:
|
||||||
|
|
||||||
cbconvert --interpolation=5 --outdir ~/.thumbnails/normal --thumbnail /media/comics/GrooTheWanderer/
|
cbconvert thumbnail --width 512 --outdir ~/.thumbnails/normal /media/comics/GrooTheWanderer/
|
||||||
|
|
||||||
|
Extract covers to ~/covers dir for all supported files found in directory, Lanczos algorithm is used for resizing:
|
||||||
|
|
||||||
|
cbconvert cover --outdir ~/covers --filter=7 /media/comics/GrooTheWanderer/
|
||||||
|
|
||||||
Compile
|
Compile
|
||||||
-------
|
-------
|
||||||
@@ -79,7 +123,9 @@ Compile latest MuPDF:
|
|||||||
|
|
||||||
git clone git://git.ghostscript.com/mupdf.git && cd mupdf
|
git clone git://git.ghostscript.com/mupdf.git && cd mupdf
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
HAVE_X11=no HAVE_GLFW=no HAVE_GLUT=no WANT_CURL=no make && make install
|
curl -L https://gist.githubusercontent.com/gen2brain/7869ac4c6db5933f670f/raw/1619394dc957ae10bcd73c713760993466b4bfea/mupdf-openssl-curl.patch | patch -p1
|
||||||
|
sed -e "1iHAVE_X11 = no" -e "1iWANT_OPENSSL = no" -e "1iWANT_CURL = no" -i Makerules
|
||||||
|
HAVE_X11=no HAVE_GLFW=no HAVE_GLUT=no WANT_OPENSSL=no WANT_CURL=no HAVE_MUJS=yes HAVE_JSCORE=no HAVE_V8=no make && make install
|
||||||
|
|
||||||
Compile unarr library:
|
Compile unarr library:
|
||||||
|
|
||||||
@@ -94,12 +140,14 @@ Compile unarr library:
|
|||||||
Install dependencies:
|
Install dependencies:
|
||||||
|
|
||||||
go get github.com/cheggaaa/pb
|
go get github.com/cheggaaa/pb
|
||||||
go get github.com/gen2brain/go-fitz
|
go get github.com/disintegration/imaging
|
||||||
|
go get github.com/gen2brain/go-fitz
|
||||||
go get github.com/gen2brain/go-unarr
|
go get github.com/gen2brain/go-unarr
|
||||||
go get github.com/gographics/imagick/imagick
|
go get github.com/gographics/imagick/imagick
|
||||||
go get github.com/hotei/bmp
|
go get github.com/hotei/bmp
|
||||||
go get github.com/nfnt/resize
|
|
||||||
go get github.com/skarademir/naturalsort
|
go get github.com/skarademir/naturalsort
|
||||||
|
go get golang.org/x/image/tiff
|
||||||
|
go get golang.org/x/image/webp
|
||||||
go get gopkg.in/alecthomas/kingpin.v2
|
go get gopkg.in/alecthomas/kingpin.v2
|
||||||
|
|
||||||
Install go package:
|
Install go package:
|
||||||
|
|||||||
+311
-118
@@ -23,7 +23,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
_ "image/gif"
|
"image/gif"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
"io"
|
"io"
|
||||||
@@ -40,41 +40,75 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/cheggaaa/pb"
|
"github.com/cheggaaa/pb"
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
"github.com/gen2brain/go-fitz"
|
"github.com/gen2brain/go-fitz"
|
||||||
"github.com/gen2brain/go-unarr"
|
"github.com/gen2brain/go-unarr"
|
||||||
"github.com/gographics/imagick/imagick"
|
"github.com/gographics/imagick/imagick"
|
||||||
_ "github.com/hotei/bmp"
|
_ "github.com/hotei/bmp"
|
||||||
"github.com/nfnt/resize"
|
|
||||||
"github.com/skarademir/naturalsort"
|
"github.com/skarademir/naturalsort"
|
||||||
|
"golang.org/x/image/tiff"
|
||||||
|
_ "golang.org/x/image/webp"
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Resample filters
|
||||||
|
const (
|
||||||
|
NearestNeighbor int = iota // Fastest resampling filter, no antialiasing
|
||||||
|
Box // Box filter (averaging pixels)
|
||||||
|
Linear // Bilinear filter, smooth and reasonably fast
|
||||||
|
MitchellNetravali // А smooth bicubic filter
|
||||||
|
CatmullRom // A sharp bicubic filter
|
||||||
|
Gaussian // Blurring filter that uses gaussian function, useful for noise removal
|
||||||
|
Lanczos // High-quality resampling filter, it's slower than cubic filters
|
||||||
|
)
|
||||||
|
|
||||||
|
var filters = map[int]imaging.ResampleFilter{
|
||||||
|
NearestNeighbor: imaging.NearestNeighbor,
|
||||||
|
Box: imaging.Box,
|
||||||
|
Linear: imaging.Linear,
|
||||||
|
MitchellNetravali: imaging.MitchellNetravali,
|
||||||
|
CatmullRom: imaging.CatmullRom,
|
||||||
|
Gaussian: imaging.Gaussian,
|
||||||
|
Lanczos: imaging.Lanczos,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options
|
||||||
|
type Options struct {
|
||||||
|
ToPNG bool // encode images to PNG instead of JPEG
|
||||||
|
ToBMP bool // encode images to 4-Bit BMP (16 colors) instead of JPEG
|
||||||
|
ToGIF bool // encode images to GIF instead of JPEG
|
||||||
|
ToTIFF bool // encode images to TIFF instead of JPEG
|
||||||
|
Quality int // JPEG image quality
|
||||||
|
Width int // image width
|
||||||
|
Height int // image height
|
||||||
|
Fit bool // Best fit for required width and height
|
||||||
|
Filter int // 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
||||||
|
RGB bool // convert images that have RGB colorspace
|
||||||
|
NonImage bool // Leave non image files in archive
|
||||||
|
Suffix string // add suffix to file basename
|
||||||
|
Cover bool // extract cover
|
||||||
|
Thumbnail bool // extract cover thumbnail (freedesktop spec.)
|
||||||
|
Outdir string // output directory
|
||||||
|
Grayscale bool // convert images to grayscale (monochromatic)
|
||||||
|
Rotate int // Rotate images, valid values are 0, 90, 180, 270
|
||||||
|
Flip string // Flip images, valid values are none, horizontal, vertical
|
||||||
|
Brightness float64 // Adjust brightness of the images, must be in range (-100, 100)
|
||||||
|
Contrast float64 // Adjust contrast of the images, must be in range (-100, 100)
|
||||||
|
Recursive bool // process subdirectories recursively
|
||||||
|
Size int64 // process only files larger then size (in MB)
|
||||||
|
Quiet bool // hide console output
|
||||||
|
}
|
||||||
|
|
||||||
|
// Globals
|
||||||
var (
|
var (
|
||||||
opts options
|
opts Options
|
||||||
workdir string
|
workdir string
|
||||||
nfiles int
|
nfiles int
|
||||||
current int
|
current int
|
||||||
|
bar *pb.ProgressBar
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command line options
|
|
||||||
type options struct {
|
|
||||||
ToPNG bool // encode images to PNG instead of JPEG
|
|
||||||
ToBMP bool // encode images to 4-Bit BMP instead of JPEG
|
|
||||||
Quality int // JPEG image quality
|
|
||||||
NoRGB bool // do not convert images with RGB colorspace
|
|
||||||
Width uint // image width
|
|
||||||
Height uint // image height
|
|
||||||
Interpolation int // 0=NearestNeighbor, 1=Bilinear, 2=Bicubic, 3=MitchellNetravali, 4=Lanczos2, 5=Lanczos3
|
|
||||||
Suffix string // add suffix to file basename
|
|
||||||
Cover bool // extract cover
|
|
||||||
Thumbnail bool // extract cover thumbnail (freedesktop spec.)
|
|
||||||
Outdir string // output directory
|
|
||||||
Recursive bool // process subdirectories recursively
|
|
||||||
Size int64 // process only files larger then size (in MB)
|
|
||||||
Quiet bool // hide console output
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command line arguments
|
// Command line arguments
|
||||||
var arguments []string
|
var arguments []string
|
||||||
|
|
||||||
@@ -90,6 +124,10 @@ func convertImage(img image.Image, index int, pathName string) {
|
|||||||
ext = "png"
|
ext = "png"
|
||||||
} else if opts.ToBMP {
|
} else if opts.ToBMP {
|
||||||
ext = "bmp"
|
ext = "bmp"
|
||||||
|
} else if opts.ToGIF {
|
||||||
|
ext = "gif"
|
||||||
|
} else if opts.ToTIFF {
|
||||||
|
ext = "tiff"
|
||||||
}
|
}
|
||||||
|
|
||||||
var filename string
|
var filename string
|
||||||
@@ -99,61 +137,77 @@ func convertImage(img image.Image, index int, pathName string) {
|
|||||||
filename = filepath.Join(workdir, fmt.Sprintf("%03d.%s", index, ext))
|
filename = filepath.Join(workdir, fmt.Sprintf("%03d.%s", index, ext))
|
||||||
}
|
}
|
||||||
|
|
||||||
var i image.Image
|
|
||||||
if opts.Width > 0 || opts.Height > 0 {
|
|
||||||
i = resize.Resize(opts.Width, opts.Height, img,
|
|
||||||
resize.InterpolationFunction(opts.Interpolation))
|
|
||||||
} else {
|
|
||||||
i = img
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.ToPNG {
|
if opts.ToPNG {
|
||||||
// convert image to PNG
|
// convert image to PNG
|
||||||
f, err := os.Create(filename)
|
if opts.Grayscale {
|
||||||
if err != nil {
|
encodeImageMagick(img, filename)
|
||||||
fmt.Fprintf(os.Stderr, "Error Create: %v\n", err.Error())
|
} else {
|
||||||
|
encodeImage(img, filename)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
png.Encode(f, i)
|
|
||||||
} else if opts.ToBMP {
|
} else if opts.ToBMP {
|
||||||
// convert image to 4-Bit - 16 colors BMP
|
// convert image to 4-Bit BMP (16 colors)
|
||||||
imagick.Initialize()
|
encodeImageMagick(img, filename)
|
||||||
|
} else if opts.ToGIF {
|
||||||
mw := imagick.NewMagickWand()
|
// convert image to GIF
|
||||||
defer mw.Destroy()
|
encodeImageMagick(img, filename)
|
||||||
|
} else if opts.ToTIFF {
|
||||||
b := new(bytes.Buffer)
|
// convert image to TIFF
|
||||||
jpeg.Encode(b, i, &jpeg.Options{jpeg.DefaultQuality})
|
encodeImage(img, filename)
|
||||||
|
|
||||||
err := mw.ReadImageBlob(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error ReadImageBlob: %v\n", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
w := imagick.NewPixelWand()
|
|
||||||
w.SetColor("black")
|
|
||||||
defer w.Destroy()
|
|
||||||
|
|
||||||
mw.SetImageBackgroundColor(w)
|
|
||||||
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE)
|
|
||||||
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE)
|
|
||||||
mw.SetImageMatte(false)
|
|
||||||
mw.SetImageCompression(imagick.COMPRESSION_NO)
|
|
||||||
mw.QuantizeImage(16, imagick.COLORSPACE_SRGB, 8, true, true)
|
|
||||||
mw.WriteImage(fmt.Sprintf("BMP3:%s", filename))
|
|
||||||
} else {
|
} else {
|
||||||
// convert image to JPEG (default)
|
// convert image to JPEG (default)
|
||||||
f, err := os.Create(filename)
|
if opts.Grayscale {
|
||||||
if err != nil {
|
encodeImageMagick(img, filename)
|
||||||
fmt.Fprintf(os.Stderr, "Error Create: %v\n", err.Error())
|
} else {
|
||||||
|
encodeImage(img, filename)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
jpeg.Encode(f, i, &jpeg.Options{opts.Quality})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<-throttle
|
<-throttle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transforms image (resize, rotate, flip, brightness, contrast)
|
||||||
|
func transformImage(img image.Image) image.Image {
|
||||||
|
var i image.Image = img
|
||||||
|
|
||||||
|
if opts.Width > 0 || opts.Height > 0 {
|
||||||
|
if opts.Fit {
|
||||||
|
i = imaging.Fit(i, opts.Width, opts.Height, filters[opts.Filter])
|
||||||
|
} else {
|
||||||
|
i = imaging.Resize(i, opts.Width, opts.Height, filters[opts.Filter])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Rotate > 0 {
|
||||||
|
switch opts.Rotate {
|
||||||
|
case 90:
|
||||||
|
i = imaging.Rotate90(i)
|
||||||
|
case 180:
|
||||||
|
i = imaging.Rotate180(i)
|
||||||
|
case 270:
|
||||||
|
i = imaging.Rotate270(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Flip != "none" {
|
||||||
|
switch opts.Flip {
|
||||||
|
case "horizontal":
|
||||||
|
i = imaging.FlipH(i)
|
||||||
|
case "vertical":
|
||||||
|
i = imaging.FlipV(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Brightness != 0 {
|
||||||
|
i = imaging.AdjustBrightness(i, opts.Brightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Contrast != 0 {
|
||||||
|
i = imaging.AdjustContrast(i, opts.Contrast)
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
// Converts PDF/EPUB/XPS document to CBZ
|
// Converts PDF/EPUB/XPS document to CBZ
|
||||||
func convertDocument(file string) {
|
func convertDocument(file string) {
|
||||||
workdir, _ = ioutil.TempDir(os.TempDir(), "cbc")
|
workdir, _ = ioutil.TempDir(os.TempDir(), "cbc")
|
||||||
@@ -166,7 +220,6 @@ func convertDocument(file string) {
|
|||||||
|
|
||||||
npages := doc.Pages()
|
npages := doc.Pages()
|
||||||
|
|
||||||
var bar *pb.ProgressBar
|
|
||||||
if !opts.Quiet {
|
if !opts.Quiet {
|
||||||
bar = pb.New(npages)
|
bar = pb.New(npages)
|
||||||
bar.ShowTimeLeft = false
|
bar.ShowTimeLeft = false
|
||||||
@@ -181,7 +234,11 @@ func convertDocument(file string) {
|
|||||||
|
|
||||||
img, err := doc.Image(n)
|
img, err := doc.Image(n)
|
||||||
|
|
||||||
if err == nil && img != nil {
|
if err == nil {
|
||||||
|
img = transformImage(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
if img != nil {
|
||||||
throttle <- 1
|
throttle <- 1
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
@@ -203,7 +260,6 @@ func convertArchive(file string) {
|
|||||||
}
|
}
|
||||||
defer archive.Close()
|
defer archive.Close()
|
||||||
|
|
||||||
var bar *pb.ProgressBar
|
|
||||||
if !opts.Quiet {
|
if !opts.Quiet {
|
||||||
bar = pb.New(ncontents)
|
bar = pb.New(ncontents)
|
||||||
bar.ShowTimeLeft = false
|
bar.ShowTimeLeft = false
|
||||||
@@ -250,18 +306,22 @@ func convertArchive(file string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.NoRGB && !isGrayScale(img) {
|
i := transformImage(img)
|
||||||
copyFile(bytes.NewReader(buf), filepath.Join(workdir, filepath.Base(pathname)))
|
|
||||||
|
if !opts.RGB && !isGrayScale(i) {
|
||||||
|
encodeImage(i, filepath.Join(workdir, filepath.Base(pathname)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if img != nil {
|
if i != nil {
|
||||||
throttle <- 1
|
throttle <- 1
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go convertImage(img, 0, pathname)
|
go convertImage(i, 0, pathname)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
copyFile(bytes.NewReader(buf), filepath.Join(workdir, filepath.Base(pathname)))
|
if opts.NonImage {
|
||||||
|
copyFile(bytes.NewReader(buf), filepath.Join(workdir, filepath.Base(pathname)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@@ -273,7 +333,6 @@ func convertDirectory(path string) {
|
|||||||
|
|
||||||
images := getImages(path)
|
images := getImages(path)
|
||||||
|
|
||||||
var bar *pb.ProgressBar
|
|
||||||
if !opts.Quiet {
|
if !opts.Quiet {
|
||||||
bar = pb.New(nfiles)
|
bar = pb.New(nfiles)
|
||||||
bar.ShowTimeLeft = false
|
bar.ShowTimeLeft = false
|
||||||
@@ -298,8 +357,10 @@ func convertDirectory(path string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.NoRGB && !isGrayScale(i) {
|
i = transformImage(i)
|
||||||
copyFile(f, filepath.Join(workdir, filepath.Base(img)))
|
|
||||||
|
if !opts.RGB && !isGrayScale(i) {
|
||||||
|
encodeImage(i, filepath.Join(workdir, filepath.Base(img)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +390,6 @@ func saveArchive(file string) {
|
|||||||
z := zip.NewWriter(zipfile)
|
z := zip.NewWriter(zipfile)
|
||||||
files, _ := ioutil.ReadDir(workdir)
|
files, _ := ioutil.ReadDir(workdir)
|
||||||
|
|
||||||
var bar *pb.ProgressBar
|
|
||||||
if !opts.Quiet {
|
if !opts.Quiet {
|
||||||
bar = pb.New(len(files))
|
bar = pb.New(len(files))
|
||||||
bar.ShowTimeLeft = false
|
bar.ShowTimeLeft = false
|
||||||
@@ -358,6 +418,95 @@ func saveArchive(file string) {
|
|||||||
z.Close()
|
z.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decodes image from reader
|
||||||
|
func decodeImage(reader io.Reader, filename string) (i image.Image, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Recovered in decodeImage %s: %v\n", filename, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
i, _, err = image.Decode(reader)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode image to file
|
||||||
|
func encodeImage(i image.Image, filename string) (err error) {
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filepath.Ext(filename) {
|
||||||
|
case ".png":
|
||||||
|
err = png.Encode(f, i)
|
||||||
|
case ".tif":
|
||||||
|
case ".tiff":
|
||||||
|
err = tiff.Encode(f, i, &tiff.Options{tiff.Uncompressed, false})
|
||||||
|
case ".gif":
|
||||||
|
err = gif.Encode(f, i, nil)
|
||||||
|
default:
|
||||||
|
err = jpeg.Encode(f, i, &jpeg.Options{opts.Quality})
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode image to file (ImageMagick)
|
||||||
|
func encodeImageMagick(i image.Image, filename string) (err error) {
|
||||||
|
imagick.Initialize()
|
||||||
|
|
||||||
|
mw := imagick.NewMagickWand()
|
||||||
|
defer mw.Destroy()
|
||||||
|
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
jpeg.Encode(b, i, &jpeg.Options{opts.Quality})
|
||||||
|
|
||||||
|
err = mw.ReadImageBlob(b.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error ReadImageBlob: %v\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Grayscale {
|
||||||
|
c := mw.GetImageColors()
|
||||||
|
mw.QuantizeImage(c, imagick.COLORSPACE_GRAY, 8, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filepath.Ext(filename) {
|
||||||
|
case ".png":
|
||||||
|
mw.SetImageFormat("PNG")
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
case ".gif":
|
||||||
|
mw.SetImageFormat("GIF")
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
case ".bmp":
|
||||||
|
w := imagick.NewPixelWand()
|
||||||
|
w.SetColor("black")
|
||||||
|
defer w.Destroy()
|
||||||
|
|
||||||
|
cs := mw.GetImageColorspace()
|
||||||
|
if opts.Grayscale {
|
||||||
|
cs = imagick.COLORSPACE_GRAY
|
||||||
|
}
|
||||||
|
|
||||||
|
mw.SetImageFormat("BMP3")
|
||||||
|
mw.SetImageBackgroundColor(w)
|
||||||
|
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE)
|
||||||
|
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE)
|
||||||
|
mw.SetImageMatte(false)
|
||||||
|
mw.SetImageCompression(imagick.COMPRESSION_NO)
|
||||||
|
mw.QuantizeImage(16, cs, 8, true, true)
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
default:
|
||||||
|
mw.SetImageFormat("JPEG")
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Lists contents of archive
|
// Lists contents of archive
|
||||||
func listArchive(file string) []string {
|
func listArchive(file string) []string {
|
||||||
var contents []string
|
var contents []string
|
||||||
@@ -511,7 +660,7 @@ func getFiles() []string {
|
|||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
if isArchive(f.Name()) || isArchive(f.Name()) {
|
if isArchive(f.Name()) || isArchive(f.Name()) {
|
||||||
if isSize(f.Size()) {
|
if isSize(f.Size()) {
|
||||||
files = append(files, f.Name())
|
files = append(files, filepath.Join(path, f.Name()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -560,6 +709,10 @@ func getImages(path string) []string {
|
|||||||
|
|
||||||
// Returns the filename that is the most likely to be the cover
|
// Returns the filename that is the most likely to be the cover
|
||||||
func getCover(images []string) string {
|
func getCover(images []string) string {
|
||||||
|
if len(images) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
for _, i := range images {
|
for _, i := range images {
|
||||||
if strings.HasPrefix(i, "cover") || strings.HasPrefix(i, "front") {
|
if strings.HasPrefix(i, "cover") || strings.HasPrefix(i, "front") {
|
||||||
return i
|
return i
|
||||||
@@ -570,7 +723,7 @@ func getCover(images []string) string {
|
|||||||
return images[0]
|
return images[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if file is comic
|
// Checks if file is archive
|
||||||
func isArchive(f string) bool {
|
func isArchive(f string) bool {
|
||||||
var types = []string{".rar", ".zip", ".7z", ".gz",
|
var types = []string{".rar", ".zip", ".7z", ".gz",
|
||||||
".bz2", ".cbr", ".cbz", ".cb7", ".cbt"}
|
".bz2", ".cbr", ".cbz", ".cb7", ".cbt"}
|
||||||
@@ -595,8 +748,8 @@ func isDocument(f string) bool {
|
|||||||
|
|
||||||
// Checks if file is image
|
// Checks if file is image
|
||||||
func isImage(f string) bool {
|
func isImage(f string) bool {
|
||||||
var types = []string{".jpg", ".jpeg", ".jpe",
|
var types = []string{".jpg", ".jpeg", ".jpe", ".png",
|
||||||
".png", ".gif", ".bmp"}
|
".gif", ".bmp", ".tiff", ".tif", ".webp"}
|
||||||
for _, t := range types {
|
for _, t := range types {
|
||||||
if strings.ToLower(filepath.Ext(f)) == t {
|
if strings.ToLower(filepath.Ext(f)) == t {
|
||||||
return true
|
return true
|
||||||
@@ -624,18 +777,6 @@ func isGrayScale(img image.Image) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes image from reader
|
|
||||||
func decodeImage(reader io.Reader, filename string) (i image.Image, err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Recovered in decodeImage %s: %v\n", filename, r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
i, _, err = image.Decode(reader)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copies reader to file
|
// Copies reader to file
|
||||||
func copyFile(reader io.Reader, filename string) error {
|
func copyFile(reader io.Reader, filename string) error {
|
||||||
os.MkdirAll(filepath.Dir(filename), 0755)
|
os.MkdirAll(filepath.Dir(filename), 0755)
|
||||||
@@ -679,8 +820,11 @@ func extractCover(file string, info os.FileInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.Width > 0 || opts.Height > 0 {
|
if opts.Width > 0 || opts.Height > 0 {
|
||||||
cover = resize.Resize(opts.Width, opts.Height, cover,
|
if opts.Fit {
|
||||||
resize.InterpolationFunction(opts.Interpolation))
|
cover = imaging.Fit(cover, opts.Width, opts.Height, filters[opts.Filter])
|
||||||
|
} else {
|
||||||
|
cover = imaging.Resize(cover, opts.Width, opts.Height, filters[opts.Filter])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filename := filepath.Join(opts.Outdir, fmt.Sprintf("%s.jpg", getBasename(file)))
|
filename := filepath.Join(opts.Outdir, fmt.Sprintf("%s.jpg", getBasename(file)))
|
||||||
@@ -712,11 +856,13 @@ func extractThumbnail(file string, info os.FileInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.Width > 0 || opts.Height > 0 {
|
if opts.Width > 0 || opts.Height > 0 {
|
||||||
cover = resize.Resize(opts.Width, opts.Height, cover,
|
if opts.Fit {
|
||||||
resize.InterpolationFunction(opts.Interpolation))
|
cover = imaging.Fit(cover, opts.Width, opts.Height, filters[opts.Filter])
|
||||||
|
} else {
|
||||||
|
cover = imaging.Resize(cover, opts.Width, opts.Height, filters[opts.Filter])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cover = resize.Resize(256, 0, cover,
|
cover = imaging.Resize(cover, 256, 0, filters[opts.Filter])
|
||||||
resize.InterpolationFunction(opts.Interpolation))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imagick.Initialize()
|
imagick.Initialize()
|
||||||
@@ -735,7 +881,7 @@ func extractThumbnail(file string, info os.FileInfo) {
|
|||||||
fileuri := "file://" + file
|
fileuri := "file://" + file
|
||||||
filename := filepath.Join(opts.Outdir, fmt.Sprintf("%x.png", md5.Sum([]byte(fileuri))))
|
filename := filepath.Join(opts.Outdir, fmt.Sprintf("%x.png", md5.Sum([]byte(fileuri))))
|
||||||
|
|
||||||
mw.SetImageFormat("png")
|
mw.SetImageFormat("PNG")
|
||||||
mw.SetImageProperty("Software", "cbconvert")
|
mw.SetImageProperty("Software", "cbconvert")
|
||||||
mw.SetImageProperty("Description", "Thumbnail of "+fileuri)
|
mw.SetImageProperty("Description", "Thumbnail of "+fileuri)
|
||||||
mw.SetImageProperty("Thumb::URI", fileuri)
|
mw.SetImageProperty("Thumb::URI", fileuri)
|
||||||
@@ -762,26 +908,57 @@ func convertComic(file string, info os.FileInfo) {
|
|||||||
|
|
||||||
// Parses command line flags
|
// Parses command line flags
|
||||||
func parseFlags() {
|
func parseFlags() {
|
||||||
opts = options{}
|
opts = Options{}
|
||||||
kingpin.Version("CBconvert 0.3.0")
|
kingpin.Version("CBconvert 0.4.0")
|
||||||
kingpin.CommandLine.Help = "Comic Book convert tool."
|
kingpin.CommandLine.Help = "Comic Book convert tool."
|
||||||
kingpin.Flag("png", "encode images to PNG instead of JPEG").Short('p').BoolVar(&opts.ToPNG)
|
kingpin.UsageTemplate(kingpin.CompactUsageTemplate)
|
||||||
kingpin.Flag("bmp", "encode images to 4-Bit BMP instead of JPEG").Short('b').BoolVar(&opts.ToBMP)
|
|
||||||
kingpin.Flag("width", "image width").Default(strconv.Itoa(0)).Short('w').UintVar(&opts.Width)
|
kingpin.Flag("outdir", "Output directory").Default(".").StringVar(&opts.Outdir)
|
||||||
kingpin.Flag("height", "image height").Default(strconv.Itoa(0)).Short('h').UintVar(&opts.Height)
|
kingpin.Flag("size", "Process only files larger then size (in MB)").Default(strconv.Itoa(0)).Int64Var(&opts.Size)
|
||||||
kingpin.Flag("quality", "JPEG image quality").Short('q').Default(strconv.Itoa(jpeg.DefaultQuality)).IntVar(&opts.Quality)
|
kingpin.Flag("recursive", "Process subdirectories recursively").BoolVar(&opts.Recursive)
|
||||||
kingpin.Flag("norgb", "do not convert images with RGB colorspace").Short('n').BoolVar(&opts.NoRGB)
|
kingpin.Flag("quiet", "Hide console output").BoolVar(&opts.Quiet)
|
||||||
kingpin.Flag("interpolation", "0=NearestNeighbor, 1=Bilinear, 2=Bicubic, 3=MitchellNetravali, 4=Lanczos2, 5=Lanczos3").Short('i').
|
|
||||||
Default(strconv.Itoa(int(resize.Bilinear))).IntVar(&opts.Interpolation)
|
convert := kingpin.Command("convert", "Convert archive or document (default command)").Default()
|
||||||
kingpin.Flag("suffix", "add suffix to file basename").Short('s').StringVar(&opts.Suffix)
|
convert.Arg("args", "filename or directory").Required().ExistingFilesOrDirsVar(&arguments)
|
||||||
kingpin.Flag("cover", "extract cover").Short('c').BoolVar(&opts.Cover)
|
convert.Flag("width", "Image width").Default(strconv.Itoa(0)).IntVar(&opts.Width)
|
||||||
kingpin.Flag("thumbnail", "extract cover thumbnail (freedesktop spec.)").Short('t').BoolVar(&opts.Thumbnail)
|
convert.Flag("height", "Image height").Default(strconv.Itoa(0)).IntVar(&opts.Height)
|
||||||
kingpin.Flag("outdir", "output directory").Default(".").Short('o').StringVar(&opts.Outdir)
|
convert.Flag("fit", "Best fit for required width and height").BoolVar(&opts.Fit)
|
||||||
kingpin.Flag("size", "process only files larger then size (in MB)").Short('m').Default(strconv.Itoa(0)).Int64Var(&opts.Size)
|
convert.Flag("quality", "JPEG image quality").Default(strconv.Itoa(jpeg.DefaultQuality)).IntVar(&opts.Quality)
|
||||||
kingpin.Flag("recursive", "process subdirectories recursively").Short('r').BoolVar(&opts.Recursive)
|
convert.Flag("filter", "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos").Default(strconv.Itoa(Linear)).IntVar(&opts.Filter)
|
||||||
kingpin.Flag("quiet", "hide console output").Short('Q').BoolVar(&opts.Quiet)
|
convert.Flag("png", "Encode images to PNG instead of JPEG").BoolVar(&opts.ToPNG)
|
||||||
kingpin.Arg("args", "filename or directory").Required().ExistingFilesOrDirsVar(&arguments)
|
convert.Flag("bmp", "Encode images to 4-Bit BMP (16 colors) instead of JPEG").BoolVar(&opts.ToBMP)
|
||||||
kingpin.Parse()
|
convert.Flag("gif", "Encode images to GIF instead of JPEG").BoolVar(&opts.ToGIF)
|
||||||
|
convert.Flag("tiff", "Encode images to TIFF instead of JPEG").BoolVar(&opts.ToTIFF)
|
||||||
|
convert.Flag("rgb", "Convert images that have RGB colorspace (use --no-rgb if you only want to convert grayscale images)").Default("true").BoolVar(&opts.RGB)
|
||||||
|
convert.Flag("nonimage", "Leave non image files in archive (use --no-nonimage to remove non image files from archive)").Default("true").BoolVar(&opts.NonImage)
|
||||||
|
convert.Flag("grayscale", "Convert images to grayscale (monochromatic)").BoolVar(&opts.Grayscale)
|
||||||
|
convert.Flag("rotate", "Rotate images, valid values are 0, 90, 180, 270").Default(strconv.Itoa(0)).IntVar(&opts.Rotate)
|
||||||
|
convert.Flag("flip", "Flip images, valid values are none, horizontal, vertical").Default("none").StringVar(&opts.Flip)
|
||||||
|
convert.Flag("brightness", "Adjust brightness of the images, must be in range (-100, 100)").Default(strconv.Itoa(0)).Float64Var(&opts.Brightness)
|
||||||
|
convert.Flag("contrast", "Adjust contrast of the images, must be in range (-100, 100)").Default(strconv.Itoa(0)).Float64Var(&opts.Contrast)
|
||||||
|
convert.Flag("suffix", "Add suffix to file basename").StringVar(&opts.Suffix)
|
||||||
|
|
||||||
|
cover := kingpin.Command("cover", "Extract cover")
|
||||||
|
cover.Arg("args", "filename or directory").Required().ExistingFilesOrDirsVar(&arguments)
|
||||||
|
cover.Flag("width", "Image width").Default(strconv.Itoa(0)).IntVar(&opts.Width)
|
||||||
|
cover.Flag("height", "Image height").Default(strconv.Itoa(0)).IntVar(&opts.Height)
|
||||||
|
cover.Flag("fit", "Best fit for required width and height").BoolVar(&opts.Fit)
|
||||||
|
cover.Flag("quality", "JPEG image quality").Default(strconv.Itoa(jpeg.DefaultQuality)).IntVar(&opts.Quality)
|
||||||
|
cover.Flag("filter", "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos").Default(strconv.Itoa(Linear)).IntVar(&opts.Filter)
|
||||||
|
|
||||||
|
thumbnail := kingpin.Command("thumbnail", "Extract cover thumbnail (freedesktop spec.)")
|
||||||
|
thumbnail.Arg("args", "filename or directory").Required().ExistingFilesOrDirsVar(&arguments)
|
||||||
|
thumbnail.Flag("width", "Image width").Default(strconv.Itoa(0)).IntVar(&opts.Width)
|
||||||
|
thumbnail.Flag("height", "Image height").Default(strconv.Itoa(0)).IntVar(&opts.Height)
|
||||||
|
thumbnail.Flag("fit", "Best fit for required width and height").BoolVar(&opts.Fit)
|
||||||
|
thumbnail.Flag("filter", "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos").Default(strconv.Itoa(Linear)).IntVar(&opts.Filter)
|
||||||
|
|
||||||
|
switch kingpin.Parse() {
|
||||||
|
case "cover":
|
||||||
|
opts.Cover = true
|
||||||
|
case "thumbnail":
|
||||||
|
opts.Thumbnail = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -791,6 +968,7 @@ func main() {
|
|||||||
signal.Notify(c, os.Interrupt, syscall.SIGHUP, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGHUP, syscall.SIGTERM)
|
||||||
go func() {
|
go func() {
|
||||||
for _ = range c {
|
for _ = range c {
|
||||||
|
fmt.Fprintf(os.Stderr, "Aborting\n")
|
||||||
os.RemoveAll(workdir)
|
os.RemoveAll(workdir)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@@ -802,6 +980,15 @@ func main() {
|
|||||||
|
|
||||||
files := getFiles()
|
files := getFiles()
|
||||||
nfiles = len(files)
|
nfiles = len(files)
|
||||||
|
|
||||||
|
if opts.Cover || opts.Thumbnail {
|
||||||
|
if !opts.Quiet {
|
||||||
|
bar = pb.New(nfiles)
|
||||||
|
bar.ShowTimeLeft = false
|
||||||
|
bar.Start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for n, file := range files {
|
for n, file := range files {
|
||||||
current = n + 1
|
current = n + 1
|
||||||
|
|
||||||
@@ -813,9 +1000,15 @@ func main() {
|
|||||||
|
|
||||||
if opts.Cover {
|
if opts.Cover {
|
||||||
extractCover(file, stat)
|
extractCover(file, stat)
|
||||||
|
if !opts.Quiet {
|
||||||
|
bar.Increment()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
} else if opts.Thumbnail {
|
} else if opts.Thumbnail {
|
||||||
extractThumbnail(file, stat)
|
extractThumbnail(file, stat)
|
||||||
|
if !opts.Quiet {
|
||||||
|
bar.Increment()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ mkdir -p build
|
|||||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o build/cbconvert
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o build/cbconvert
|
||||||
strip build/cbconvert
|
strip build/cbconvert
|
||||||
|
|
||||||
#CGO_LDFLAGS="-lm -lz -ldl -lltdl -lfreetype -static-libgcc" CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -v -x -o build/cbconvert-static --ldflags '-extldflags "-static"'
|
CGO_LDFLAGS="-ldl -lltdl -lfreetype -lm -lz -static-libgcc" CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o build/cbconvert-static --ldflags '-extldflags "-static"'
|
||||||
#strip build/cbconvert-static
|
strip build/cbconvert-static
|
||||||
|
|
||||||
CGO_LDFLAGS="-L/usr/i686-pc-mingw32/usr/lib" \
|
CGO_LDFLAGS="-L/usr/i686-pc-mingw32/usr/lib" \
|
||||||
CGO_CFLAGS="-I/usr/i686-pc-mingw32/usr/include -Wno-poison-system-directories" \
|
CGO_CFLAGS="-I/usr/i686-pc-mingw32/usr/include -Wno-poison-system-directories" \
|
||||||
|
|||||||
Reference in New Issue
Block a user