diff --git a/documentTypes.plist b/documentTypes.plist index babe49f..e4e7ba6 100644 --- a/documentTypes.plist +++ b/documentTypes.plist @@ -197,4 +197,60 @@ LSTypeIsPackage + + CFBundleTypeExtensions + + cbz + CBZ + + CFBundleTypeIconFile + cbz.icns + CFBundleTypeMIMETypes + + application/vnd.comicbook+zip + application/x-cbz + + CFBundleTypeName + Comic Book Zip Archive + CFBundleTypeOSTypes + + CBZ + + CFBundleTypeRole + Viewer + LSItemContentTypes + + in.yetaga.why.cbz + + LSTypeIsPackage + + + + CFBundleTypeExtensions + + cbr + CBR + + CFBundleTypeIconFile + cbr.icns + CFBundleTypeMIMETypes + + application/vnd.comicbook-rar + application/x-cbr + + CFBundleTypeName + Comic Book Rar Archive + CFBundleTypeOSTypes + + CBR + + CFBundleTypeRole + Viewer + LSItemContentTypes + + in.yetaga.why.cbr + + LSTypeIsPackage + + \ No newline at end of file diff --git a/filetypes/filetypes.go b/filetypes/filetypes.go index 96eb4dd..98ccf8f 100644 --- a/filetypes/filetypes.go +++ b/filetypes/filetypes.go @@ -84,6 +84,24 @@ var Valid = []FileDescription{ OSTypes: []string{"WEBP"}, ItemContentTypes: "org.webmproject.webp", }, + { + TkTypeName: "CBZ", + MacExtensions: []string{"cbz", "CBZ"}, + IconFile: "cbz.icns", + MIMETypes: []string{"application/vnd.comicbook+zip", "application/x-cbz"}, + TypeName: "Comic Book Zip Archive", + OSTypes: []string{"CBZ"}, + ItemContentTypes: "in.yetaga.why.cbz", + }, + { + TkTypeName: "CBR", + MacExtensions: []string{"cbr", "CBR"}, + IconFile: "cbr.icns", + MIMETypes: []string{"application/vnd.comicbook-rar", "application/x-cbr"}, + TypeName: "Comic Book Rar Archive", + OSTypes: []string{"CBR"}, + ItemContentTypes: "in.yetaga.why.cbr", + }, } func GetTkTypes(fds []FileDescription) []tk.FileType { diff --git a/main.go b/main.go index f4fb674..d2da182 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,11 @@ package main import ( + "archive/zip" _ "embed" "fmt" "image" + "io" "log" "os" "path/filepath" @@ -22,7 +24,12 @@ import ( type state struct { dir string i int - images []string + images []imagefile +} + +type imagefile struct { + contents *image.Image + filename string } //go:embed media/noise.png @@ -41,7 +48,20 @@ var fileListBindVar = tk.Variable("FileList") var directoryState state func (d state) pathToImageAtIndex(i int) string { - return filepath.Join(directoryState.dir, directoryState.images[i]) + return filepath.Join(directoryState.dir, directoryState.images[i].filename) +} + +func (d state) imageWithFilename(s string) (imagefile, int, error) { + for i, f := range directoryState.images { + if f.filename == s { + return directoryState.images[i], i, nil + } + } + return imagefile{}, 0, fmt.Errorf("not found") +} + +func (d state) setImage(i int, img *image.Image) { + d.images[i].contents = img } func must[T any](t T, err error) T { @@ -62,7 +82,49 @@ func newFileInDirectory() { return } // GetOpenFile returns an array split on spaces! - newBrowse(strings.Join(files, " ")) + file := strings.Join(files, " ") + if filepath.Ext(file) == ".cbr" || filepath.Ext(file) == ".cbz" { + log.Println("Comic Book Archive!") + openArchive(file) + return + } + newBrowse(file) +} + +func openArchive(path string) { + r, err := zip.OpenReader(path) + if err != nil { + log.Println(err) + return + } + clearFileList() + directoryState.images = []imagefile{} + for _, f := range r.File { + log.Println(f.FileInfo().Name()) + if f.FileInfo().IsDir() { + continue + } + if f.FileInfo().Name()[0] == '.' { + continue + } + reader, err := f.Open() + if err != nil { + log.Println(err) + continue + } + i, err := decode(reader, f.FileInfo().Name()) + if err != nil { + log.Println(err) + continue + } + directoryState.images = append(directoryState.images, imagefile{ + contents: &i, + filename: f.FileInfo().Name(), + }) + insertIntoFileList(f.FileInfo().Name()) + } + moveSelectionInFileList(0) + updateImage(directoryState.images[0].filename) } func newDirectory() { @@ -102,12 +164,12 @@ func newBrowse(file string) { }) // ...that way we only have to do this loop once. clearFileList() - directoryState.images = []string{} + directoryState.images = []imagefile{} i := 0 for _, v := range dirfiles { if filetypes.IsImage(v) { - directoryState.images = append(directoryState.images, v.Name()) - insertIntoFileList(directoryState.images[i]) + directoryState.images = append(directoryState.images, imagefile{filename: v.Name()}) + insertIntoFileList(directoryState.images[i].filename) if v.Name() == filepath.Base(file) { directoryState.i = i moveSelectionInFileList(i) @@ -121,24 +183,28 @@ func newBrowse(file string) { func updateImage(file string) { var i image.Image - f, err := os.Open(file) + entry, index, err := directoryState.imageWithFilename(filepath.Base(file)) if err != nil { - log.Printf("error opening image: %v", err) + log.Println(err.Error()) return } - defer f.Close() + if entry.contents != nil { + fmt.Printf("loaded %s from cache\n", file) + i = *entry.contents + } else { + f, err := os.Open(file) + if err != nil { + log.Printf("error opening image: %v", err) + return + } + defer f.Close() - ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(file), ".")) - if checkErr(imaging.FormatFromExtension(ext)) { - i, err = imaging.Decode(f, imaging.AutoOrientation(true)) - } else if ext == "webp" { - i, err = webp.Decode(f) - } else if ext == "tga" { - i, err = tga.Decode(f) - } - if err != nil { - log.Printf("error decoding image: %v", err) - return + i, err = decode(f, file) + if err != nil { + log.Printf("error decoding image: %v", err) + return + } + directoryState.setImage(index, &i) } i = imaging.Fit(i, // -50 to give some space to breathe around the edges @@ -149,6 +215,18 @@ func updateImage(file string) { repaint(filepath.Base(file), tk.Data(i)) } +func decode(f io.Reader, name string) (image.Image, error) { + ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(name), ".")) + if checkErr(imaging.FormatFromExtension(ext)) { + return imaging.Decode(f, imaging.AutoOrientation(true)) + } else if ext == "webp" { + return webp.Decode(f) + } else if ext == "tga" { + return tga.Decode(f) + } + return image.Black, fmt.Errorf("could not parse image") +} + func repaint(name string, data tk.Opt) { // TODO: sometimes, when going from a big image to a smaller one, // the window remains the same height as the big image, @@ -229,7 +307,7 @@ func constructFileList() { })) clearFileList() for i := range directoryState.images { - insertIntoFileList(directoryState.images[i]) + insertIntoFileList(directoryState.images[i].filename) if directoryState.i == i { moveSelectionInFileList(i) }