lackey

package module
v0.8.1 Latest Latest
Warning

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

Go to latest
Published: Jan 10, 2023 License: MIT Imports: 17 Imported by: 0

README

lackey

The primary purpose that lackey is to manage a lower-quality version of your music library. This comes in handy when you want to compress your collection of FLACs to something you can put on your phone (for example).

The lackey program is distributed under the MIT License.

Installation

If you have Go installed, installing lackey is easy:

go get -u github.com/cassava/lackey
cd $GOPATH/src/github.com/cassava/lackey
go install ./cmd/...
Usage

At the moment, lackey's functionality is quite basic. It comes with several commands and flags that you can use. There are four commands:

  • sync – synchronizes from your high-quality music library to a lower-quality mirror
  • stats – reads a library and prints information about it (this may not be particularly useful for you, but it is for me as the dev)
  • version – shows the version and compilation date of lackey
  • help – shows the help for lackey and the other commands

Let's say your music library is at ~/music, and you would like a mirror at ~/music2go. Then we would use lackey like this:

lackey sync --library=~/music --delete-before ~/music2go

This will by default use the following options (as shown by lackey help sync):

  • it will follow symlinks (--follow-symlinks=true)
  • it will unconditionally convert non-MP3 music with the LAME encoder with a quality setting of 4 (--quality=4). See the LAME encoder on what the quality setting means. Lower is better.
  • it will convert existing MP3s if they have a bitrate higher than 256kbps, and copy them otherwise (--threshold=256)
  • it will copy all data files that are not music
  • it will delete all unexpected files in the destination (like rsync does it, essentially)
  • it will use the number of cores as the number of workers to use (--concurrent=4)

Note that reading in the library can take a few minutes (I hope to improve this in the future); it's insignificant compared to converting and copying files, but you probably don't want to do this when there's nothing to do.

With these settings, I can reduce a 110GB library to about 30GB. If you want it to take up even less space, you can increase the quality setting and reduce the threshold at which it is converted. If you want to configure even more, let me know and I'll see what I can do.

With Docker

There's a Docker image now that lets you easily automate this task in a job.

Build the image first:

docker build -t lackey:latest .

Run the image like so:

docker run --rm -it \
  -v $(pwd)/hifi:/mnt/hifi:ro \
  -v $(pwd)/lofi:/mnt/lofi \
  lackey:latest \
  lackey -L /mnt/hifi sync -s --cover-target folder.jpg -m -d --bitrate 192k --opus --threshold 192 /mnt/lofi

Of course, adjust the parameters as required. This worked pretty well for me. Note that I mounted my source directory as read-only (:ro), which protects it from mistakes in lackey as well as mistakes I might make in the call. I highly highly recommend you protect your source material in one way or another.

If you want to run the image as a service, you can do that too:

docker run --rm \
  -v $(pwd)/hifi:/mnt/hifi:ro \
  -v $(pwd)/lofi:/mnt/lofi \
  -p 8080:8080 \
  lackey:latest

This will spawn a service that runs continuously in the background and lets you interactively spawn a job by visiting the port in a browser.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Skip = errors.New("skip the directory or remaining files in directory")

Functions

This section is empty.

Types

type Audio

type Audio interface {
	IsExists() bool
	FileInfo() os.FileInfo
	Encoding() audio.Codec
	Metadata() audio.Metadata
}

type AudioOperation

type AudioOperation int
const (
	SkipAudio AudioOperation = iota
	IgnoreAudio
	TranscodeAudio
	UpdateAudio
	CopyAudio
)

type Database

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

func ReadLibrary

func ReadLibrary(path string) (*Database, error)

ReadLibrary reads a library using the recommended options, namely:

FollowSymlinks = true
IgnoreHidden   = true

func (*Database) Get

func (db *Database) Get(key string) *Entry

Get returns the entry that has the given key, or nil.

func (*Database) Path

func (db *Database) Path() string

func (*Database) Root

func (db *Database) Root() *Entry

func (*Database) Set

func (db *Database) Set(key string, e *Entry)

func (*Database) Size

func (db *Database) Size() int64

func (*Database) Walk

func (db *Database) Walk(fn func(e *Entry) error) error

type Encoder

type Encoder interface {
	Ext() string
	CanCopy(src, dst Audio) bool
	Encode(src, dst string, md Audio) error
}

type Entry

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

func (*Entry) AbsPath

func (e *Entry) AbsPath() string

func (*Entry) Children

func (e *Entry) Children() []*Entry

func (*Entry) Data

func (e *Entry) Data() interface{}

Care should be taken with when Data is called, as the first call may involve reading the metadata of the associated file.

func (*Entry) Encoding

func (e *Entry) Encoding() audio.Codec

func (*Entry) FileInfo

func (e *Entry) FileInfo() os.FileInfo

func (*Entry) Filename

func (e *Entry) Filename() string

func (*Entry) FilenameExt

func (e *Entry) FilenameExt() (base string, ext string)

func (*Entry) IsDir

func (e *Entry) IsDir() bool

func (*Entry) IsExists

func (e *Entry) IsExists() bool

func (*Entry) IsIgnored

func (e *Entry) IsIgnored() bool

func (*Entry) IsMusic

func (e *Entry) IsMusic() bool

func (*Entry) Key

func (e *Entry) Key() string

func (*Entry) Metadata

func (e *Entry) Metadata() audio.Metadata

func (*Entry) Parent

func (e *Entry) Parent() *Entry

func (*Entry) RelPath

func (e *Entry) RelPath() string

func (*Entry) RootPath

func (e *Entry) RootPath() string

func (*Entry) Size

func (e *Entry) Size() int64

Size returns the size of the entry in bytes. A directory's size is the size of all its children.

func (*Entry) Type

func (e *Entry) Type() EntryType

func (*Entry) Walk

func (e *Entry) Walk(fn func(e *Entry) error) error

type EntryType

type EntryType int
const (
	DirEntry EntryType = 1 << iota
	FileEntry
	MusicEntry
	IgnoreEntry

	ErrorEntry EntryType = 0
)

type ExecError

type ExecError struct {
	Err    error
	Output string
}

func (*ExecError) Error

func (err *ExecError) Error() string

type LibraryReader

type LibraryReader struct {
	FollowSymlinks bool
	IgnoreHidden   bool
}

func (LibraryReader) ReadLibrary

func (r LibraryReader) ReadLibrary(path string) (*Database, error)

type MP3Encoder

type MP3Encoder struct {
	TargetQuality    int
	BitrateThreshold int
}

func (*MP3Encoder) CanCopy

func (e *MP3Encoder) CanCopy(src, dst Audio) bool

func (*MP3Encoder) Encode

func (e *MP3Encoder) Encode(src, dst string, md Audio) error

func (*MP3Encoder) Ext

func (e *MP3Encoder) Ext() string

type OPUSEncoder

type OPUSEncoder struct {
	Extension     string
	TargetBitrate string
}

func (*OPUSEncoder) CanCopy

func (e *OPUSEncoder) CanCopy(src, dst Audio) bool

func (*OPUSEncoder) Encode

func (e *OPUSEncoder) Encode(src, dst string, md Audio) error

func (*OPUSEncoder) Ext

func (e *OPUSEncoder) Ext() string

type Operator

type Operator interface {
	// WhichExt takes the source metadata and returns the
	// expected destination extension of the file, such as ".mp3".
	// If "" is returned, the extension remains unchanged.
	//
	// Notes:
	// - This determines the destination filename that is constructed.
	//   Because the Operator takes care of encoding, it does not
	//   determine the encoding process.
	WhichExt(src Audio) string

	// Which returns an audio operation that should be
	// performed, based on src and dst (possibly nil).
	//
	// Notes:
	// - The Operator has full freedom in this decision.
	// - Parameter src is not nil.
	// - If dst == nil, then the destination file does not exist.
	// - If IgnoreAudio is returned, then Operator.Ignore is called.
	// - If UpdateAudio is returned, then Operator.Update is called.
	// - If CopyAudio is returned, then Operator.CopyFile is called.
	Which(src, dst Audio) AudioOperation

	// Feedback
	Ok(dst string) error
	Ignore(dst string) error
	Error(err error) error
	Warn(err error) error

	// Operations
	RemoveDir(dst string) error
	CreateDir(dst string) error

	// RemoveFile removes a file from the destination.
	// This occurs primarily when there is no corresponding source file or directory.
	RemoveFile(dst string) error
	CopyFile(src, dst string) error
	Transcode(src, dst string, md Audio) error
	Update(src, dst string, md Audio) error
	DownscaleCover(src, dst string) error
}

type Planner

type Planner struct {
	IgnoreData bool
	DataExcept map[string]bool

	DeleteBefore bool
	TranscodeAll bool
	Concurrent   int

	DownscaleCover bool
	CoverSource    string
	CoverTarget    string
	// contains filtered or unexported fields
}

func NewPlanner

func NewPlanner(src, dst *Database, op Operator) *Planner

func (*Planner) Plan

func (p *Planner) Plan() error

type Runner

type Runner struct {
	Color *color.Colorizer

	Encoder        Encoder
	ForceTranscode bool
	CopyExtensions []string

	DryRun    bool
	Verbose   bool
	Strip     bool
	SrcPrefix string
	DstPrefix string
}

func (*Runner) CopyFile

func (o *Runner) CopyFile(src, dst string) error

func (*Runner) CreateDir

func (o *Runner) CreateDir(dst string) error

func (*Runner) DownscaleCover added in v0.7.0

func (o *Runner) DownscaleCover(src, dst string) error

func (*Runner) Error

func (o *Runner) Error(err error) error

func (*Runner) Ignore

func (o *Runner) Ignore(dst string) error

func (*Runner) Ok

func (o *Runner) Ok(dst string) error

func (*Runner) RemoveDir

func (o *Runner) RemoveDir(dst string) error

func (*Runner) RemoveFile

func (o *Runner) RemoveFile(dst string) error

func (*Runner) Transcode

func (o *Runner) Transcode(src, dst string, md Audio) error

func (*Runner) Update

func (o *Runner) Update(src, dst string, md Audio) error

func (*Runner) Warn

func (o *Runner) Warn(err error) error

func (*Runner) Which

func (o *Runner) Which(src, dst Audio) AudioOperation

func (*Runner) WhichExt

func (o *Runner) WhichExt(src Audio) string

Directories

Path Synopsis
audio
mp3
cmd
lackey command
rtag command
Package filetype categorizes files based on their filetype, with the exception of directories, which it identifies directly.
Package filetype categorizes files based on their filetype, with the exception of directories, which it identifies directly.

Jump to

Keyboard shortcuts

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