tanuki

package module
v2.0.0-...-1da1122 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2025 License: BSD-3-Clause Imports: 40 Imported by: 0

README

tanuki

Self-hosted OPDS manga server

FAQ

Q: What features does it have?

  • OPDS API
  • Multiple user accounts
  • Support for:
    • .zip and .cbz archives
    • .jpeg, .png, .webp, .tiff and .bmp images
  • Nested folders in library

Q: What's the OPDS support like?

  • The route for the OPDS catalog is /opds/v1.2/catalog
  • This is the current OPDS 1.2 feature support:
    • Basic Auth
    • Catalog feed
    • Downloading of archives
    • Getting cover/thumbnail of entries
    • Searching (via OpenSearch)
    • Page streaming

Q: Does it have a CLI?

Yes.

$ tanuki -help
Usage of tanuki:
  -config string
        Path to config.json file. Leave blank to use the default config

$ tanukictl -help
Usage: tanukictl [options] [command] args

Options:
  -host string
        Host address of tanuki (default "0.0.0.0")
  -port string
        Port tanuki's RPC handler is listening on (default "9001")

Commands:
  scan                                  Scan the library
  dump                                  Dump the store's state
  user add <name>                       Add a new user with the password provided via stdin
  user delete <name>                    Delete an existing user
  user edit name <old-name> <new-name>  Change a user's name
  user edit pass <name>                 Change a user's password provided via stdin

  $ tanukictl -port 5000 scan
  $ tanukictl -port 5000 dump
    // Scan the library, then dump the store's contents
    // We connect to a tanuki instance listening on a
    // standard host but a non-standard port (5000)

  $ tanukictl user edit name old-name new-name
  $ echo "new-password" | tanukictl user edit pass new-name
    // Edit a user's name, then their password

Q: What does the config file look like?

This is TOML-encoded.

host = '0.0.0.0'
http_port = 8001
rpc_port = 9001
data_path = './data'
library_path = './library'
scan_interval = '1h0m0s'
log_level = 'DEBUG' # One of DEBUG, INFO, WARN, ERROR

Q: Where's my username and password?

The default username and password are logged to STDERR on the initialisation of the store.

To change the password, run echo "new-password" | tanukictl user edit pass name. This works assuming that you are using the default configuration.

Q: I'm not using the default config, how do I connect to the server?

You can manually specify the host and port that the tanuki RPC handler has bound to, via -host and -port.

Q: Do you support standalone files?

No, if you want to add an entry to the library it must exist within its own folder.

Q: Should I expose the RPC port?

No! It is not protected by any authentication mechanisms.

Q: Can I run this using Docker?

Yes, look at the following compose.yaml file.

Notice that for this setup to work, you must create the config file before the container is created, otherwise Docker attempts to create it as a directory.

---
services:
  tanuki:
    image: ghcr.io/fiwippi/tanuki:latest
    command: -config /config.toml
    ports:
      - "8001:8001"
    volumes:
      - ./config.toml:/config.toml
      - ./library/:/library
      - ./data:/data

Q: How can I run RPC commands with Docker?

First attach to the container.

$ docker exec -it tanuki /bin/sh

Now you can run commands as you please.

$ tanukictl scan
Scan complete in 2ms

Documentation

Index

Constants

View Source
const InMemory string = "file::memory:"

Variables

View Source
var Version = "dev"

We override the version during build time

Functions

func ParseLibrary

func ParseLibrary(path string) (map[Series][]Entry, error)

func ParseSeries

func ParseSeries(path string) (Series, []Entry, error)

func Sha256

func Sha256(s string) string

Types

type ChangePasswordRequest

type ChangePasswordRequest struct {
	Name, Password string
}

type ChangeUsernameRequest

type ChangeUsernameRequest struct {
	OldName, NewName string
}

type Entry

type Entry struct {
	EID      string
	SID      string
	Title    string
	ModTime  time.Time
	Archive  string
	Filesize int64
	Pages    Pages
}

func ParseEntry

func ParseEntry(path string) (Entry, error)

type Page

type Page struct {
	Path string
	Mime string
	// Was the path originally encoded using a non-UTF-8
	// encoding? The only alternate encoding we support
	// is CP437
	NonUtf8 bool
}

type Pages

type Pages []Page

func (*Pages) Scan

func (p *Pages) Scan(src any) error

func (Pages) Value

func (p Pages) Value() (driver.Value, error)

type ParseError

type ParseError struct {
	Items []ParseErrorItem
}

func (*ParseError) Error

func (pe *ParseError) Error() string

type ParseErrorItem

type ParseErrorItem struct {
	Name string
	Err  error
}

type Series

type Series struct {
	SID     string
	Title   string
	Author  string
	ModTime time.Time
}

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(config ServerConfig) (*Server, error)

func (*Server) AddUser

func (s *Server) AddUser(u User, _ *struct{}) error

func (*Server) ChangePassword

func (s *Server) ChangePassword(req ChangePasswordRequest, _ *struct{}) error

func (*Server) ChangeUsername

func (s *Server) ChangeUsername(req ChangeUsernameRequest, _ *struct{}) error

func (*Server) DeleteUser

func (s *Server) DeleteUser(name string, _ *struct{}) error

func (*Server) Dump

func (s *Server) Dump(_ struct{}, output *string) error

func (*Server) Scan

func (s *Server) Scan(_ struct{}, _ *struct{}) error

func (*Server) Start

func (s *Server) Start() error

func (*Server) Stop

func (s *Server) Stop()

type ServerConfig

type ServerConfig struct {
	Host         string   `toml:"host"`
	HttpPort     uint16   `toml:"http_port"`
	RpcPort      uint16   `toml:"rpc_port"`
	DataPath     string   `toml:"data_path"`
	LibraryPath  string   `toml:"library_path"`
	ScanInterval duration `toml:"scan_interval"`
	LogLevel     string   `toml:"log_level"`
}

func DefaultServerConfig

func DefaultServerConfig() ServerConfig

type Store

type Store struct {
	// contains filtered or unexported fields
}

func NewStore

func NewStore(path string) (*Store, error)

func (*Store) AddUser

func (s *Store) AddUser(name, pass string) error

func (*Store) AuthLogin

func (s *Store) AuthLogin(name, pass string) bool

func (*Store) ChangePassword

func (s *Store) ChangePassword(name, pass string) error

func (*Store) ChangeUsername

func (s *Store) ChangeUsername(oldName, newName string) error

func (*Store) Close

func (s *Store) Close() error

func (*Store) DeleteUser

func (s *Store) DeleteUser(name string) error

func (*Store) Dump

func (s *Store) Dump() (string, error)

func (*Store) GetCatalog

func (s *Store) GetCatalog() ([]Series, error)

func (*Store) GetEntries

func (s *Store) GetEntries(sid string) ([]Entry, error)

func (*Store) GetEntry

func (s *Store) GetEntry(sid, eid string) (Entry, error)

func (*Store) GetPage

func (s *Store) GetPage(sid, eid string, pageNum int) (*bytes.Buffer, string, error)

func (*Store) GetSeries

func (s *Store) GetSeries(sid string) (Series, error)

func (*Store) GetThumbnail

func (s *Store) GetThumbnail(sid, eid string) (*bytes.Buffer, string, error)

func (*Store) PopulateCatalog

func (s *Store) PopulateCatalog(input map[Series][]Entry) error

func (*Store) Vacuum

func (s *Store) Vacuum() error

type User

type User struct {
	Name string
	Pass string // Hashed
}

Directories

Path Synopsis
cmd
tanuki command
tanukictl command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL