Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include README LICENSE
include README.md LICENSE
123 changes: 52 additions & 71 deletions README → README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
======
brutha
======
# brutha

Description
-----------
## Description

*brutha* is an answer to the utter failure of sound conversion tools.

Expand All @@ -13,15 +10,15 @@ to a provided directory, with a "synchronization" behavior.
By carefully keeping the files' timestamps, it also allows that destination
to be easily rsynced elsewhere, and thus avoid any useless transfers.

Due to the usage of formats with Ogg containers, no tags are lost in the process of converting FLAC to Ogg Vorbis.
Due to the usage of formats with Ogg containers, no tags are lost in the
process of converting FLAC to Ogg Vorbis.

Since transcoding requires a lot of CPU time, it is useful to run
as many jobs as possible in parallel. `brutha` will try to make use of
powerful, time-tested tools (either `GNU make`, `GNU parallel`,
as many jobs as possible in parallel. *brutha* will try to make use of
powerful, time-tested tools (either *GNU make*, *GNU parallel*,
or similar implementations) to do so.
It's not perfect but good enough while keeping it simple.


To our knowledge, no other solution can fully:

* avoid encoding again files that were already processed at an earlier run
Expand All @@ -34,118 +31,102 @@ To our knowledge, no other solution can fully:
* save space for lossy files by using hardlinks or reflinks
* not be an annoying GUI

To sum up, `brutha` is a music converter tailored to audiophiles,
To sum up, *brutha* is a music converter tailored to audiophiles,
who want to convert their huge collections to more portable destinations,
with the highest quality/size ratio in mind.

`brutha` is very simple and rests on the shoulders on giants:
*brutha* is very simple and rests on the shoulders of giants:
FLAC, Ogg Vorbis, sox, Python, mutagen, GNU Make, GNU Parallel, Bash, etc.

## Requirements

Requirements
------------

The first one is space. Since you can store locally all the source files, we suppose you
also can store the smaller destination files locally. Alternatively,
you will be able to sync to a portable player, a remote filesystem (NFS, sshfs, etc.)
without issues except lower performance.

Software requirements
~~~~~~~~~~~~~~~~~~~~~

* Python 2 (2.6 or later)
* python-argparse (or Python 2.7 or later)
* `python-mutagen <http://pypi.python.org/pypi/mutagen>`_
* `sox <http://sox.sourceforge.net/>`_
* for parallel runs, either `GNU make <http://www.gnu.org/software/make/make.html>`_
or `GNU parallel <http://www.gnu.org/software/parallel/>`_, or a similar implementation

Optional :
The first one is space. Since you can store locally all the source
files, we suppose you also can store the smaller destination files
locally. Alternatively, you will be able to sync to a portable player, a
remote filesystem (NFS, sshfs, etc.) without issues except lower
performance.

* `vorbisgain <https://sjeng.org/vorbisgain.html>`_ for ``--gain``
### Software requirements

* Python 3 (3.2 or later)
* [python-mutagen](http://pypi.python.org/pypi/mutagen)
* [sox](http://sox.sourceforge.net/)
* for parallel runs, either
[GNU make](http://www.gnu.org/software/make/make.html) or
[GNU parallel](http://www.gnu.org/software/parallel/) or
a similar implementation

Usage
-----
Optional:
* [vorbisgain](https://sjeng.org/vorbisgain.html) for `--gain`

::
## Usage

brutha [options] SOURCE DESTINATION

``brutha -h`` provides help for all available options.
`brutha -h` provides help for all available options.

You can run ``python -m brutha`` to use it without installing.
You can run `python -m brutha` to use it without installing.

Default values
~~~~~~~~~~~~~~
`brutha` tries to detect how many cores you have
(run ``brutha -h`` to check the default for ``-j``).
### Default values

*brutha* tries to detect how many cores you have
(run `brutha -h` to check the default for `-j`).
It also tries to use a parallel method
(``make`` or ``parallel`` instead of ``sh``) if available.
(`make` or `parallel` instead of `sh`) if available.

By default, it does not run or delete anything; when you are experienced
with its usage, you will likely call it with ``-x`` (execute) and ``-d`` (delete).
with its usage, you will likely call it with `-x` (execute) and `-d` (delete).

Examples
~~~~~~~~
### Examples

A typical use would be::
A typical use would be:

brutha -d -x -q6 -R44100 -B16 ~/Music /mnt/portable_music_player/Music

This downsamples music to 16/44 as most portable players don't handle
24/96 well (-R44100 -B16), encodes FLAC to Ogg at a reasonable quality (-q6),
deletes old unwanted files (-d), and executes the commands right away (-x).

Recommendations
~~~~~~~~~~~~~~~
### Recommendations

Since encoding eats a lot of CPU, you should start it at a low priority.
The simplest way is to run ``nice -n19 brutha`` instead of only ``brutha``.
The simplest way is to run `nice -n19 brutha` instead of only *brutha*.

Symbolic links
~~~~~~~~~~~~~~
brutha ignores symbolic links by default, but can follow links outside of the
source path, or inside the source path, using options ``--outside`` and ``--inside``.
### Symbolic links

This is modelled after mpd's ``follow_outside_symlinks`` and ``follow_inside_symlinks`` options.
brutha ignores symbolic links by default, but can follow links outside
of the source path, or inside the source path, using options `--outside`
and `--inside`.

Changes
-------
This is modelled after mpd's `follow_outside_symlinks` and
`follow_inside_symlinks` options.

+ 1.1.1
## Changes

+ 1.1.1
- Fix some corner cases with directory walking.

+ 1.1.0

- Add sox option to guard against clipping.
- Add options to create hardlinks or reflinks.
- Make parallel the default if available.
- Code and documentation improvements.
- Support for newer parallel versions.

+ 1.0.2

- Code improvements.
- Show defaults in command-line help.

+ 1.0.1

- Bugfixes.

## Future


Future
------

`brutha` is considered feature-complete.
Python 3 support is waiting for mutagen.
*brutha* is considered feature-complete.

If you want to help, here are some possibilities:

* Support other formats (currently only FLAC to Ogg Vorbis, with Ogg Vorbis and MP3 as exact copies).
* Support other formats (currently only FLAC to Ogg Vorbis, with Ogg
Vorbis and MP3 as exact copies).
* Make mutagen optional (only required for frequency / bit depth checks).
* Port mutagen to Python 3
* Support downmixing (5.1 to 2.0 for instance)

Contributions can be sent in the form of git patches, to laurent@bachelier.name.
Contributions can be sent in the form of git patches, to
<laurent@bachelier.name>.
6 changes: 2 additions & 4 deletions brutha/__main__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function

import argparse
import sys
from multiprocessing import cpu_count
from StringIO import StringIO
from io import StringIO

from .output import OUTPUTS
from .tree import Tree
Expand All @@ -26,7 +24,7 @@ def main():
help="Compute ReplayGain if missing")
parser.add_argument('-d', '--delete', action='store_true',
help="Delete extraneous files in destination")
parser.add_argument('-o', '--output', default=output, choices=OUTPUTS.keys(),
parser.add_argument('-o', '--output', default=output, choices=list(OUTPUTS.keys()),
help="Command list type")
parser.add_argument('-R', '--maxrate', type=int,
help="Maximum sample rate allowed (e.g. 44100)", metavar="RATE")
Expand Down
2 changes: 0 additions & 2 deletions brutha/directory.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import

import os
import re

Expand Down
2 changes: 0 additions & 2 deletions brutha/file.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import

import os
import re
from datetime import datetime
Expand Down
8 changes: 3 additions & 5 deletions brutha/output.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function

import subprocess

from .util import require_executable
Expand Down Expand Up @@ -48,7 +46,7 @@ def run(self, stream):
assert line.startswith('#!')
shebang = line[2:].split()
p = subprocess.Popen(shebang, shell=False, stdin=subprocess.PIPE)
p.communicate(stream.getvalue())
p.communicate(stream.getvalue().encode())
return p.returncode


Expand Down Expand Up @@ -88,7 +86,7 @@ def _escape(self, line):

def write(self, commands, stream):
prefix = '' if self.echo else '@'
targets = ' '.join('d%s' % i for i in xrange(0, len(commands)))
targets = ' '.join('d%s' % i for i in range(0, len(commands)))
print('.PHONY: all %s' % targets, file=stream)
print('all: %s' % targets, file=stream)
print(file=stream)
Expand All @@ -105,7 +103,7 @@ def run(self, stream):
addon = []
p = subprocess.Popen([require_executable('make', ['gmake', 'make']), '-f', '-'] + addon,
shell=False, stdin=subprocess.PIPE)
p.communicate(stream.getvalue())
p.communicate(stream.getvalue().encode())
return p.returncode


Expand Down
2 changes: 0 additions & 2 deletions brutha/tree.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function

import os

from .directory import Directory, NotInteresting
Expand Down
2 changes: 0 additions & 2 deletions brutha/util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import

import os


Expand Down
12 changes: 5 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,23 @@

from setuptools import setup

assert version_info >= (2, 6)
assert version_info >= (3, 2) # 'argparse' module requires Python 3.2 or later
requirements = ['mutagen']
if version_info < (2, 7):
requirements.append('argparse')

setup(
name='brutha',
version='1.1.1',
description='Sync FLAC music files to Ogg Vorbis (or keep lossy as-is)',
long_description=open('README').read(),
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
author='Laurent Bachelier',
author_email='laurent@bachelier.name',
url='http://git.p.engu.in/laurentb/brutha/',
packages=['brutha'],
install_requires=requirements,
classifiers=[
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Operating System :: POSIX',
'Environment :: Console',
'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)',
Expand Down