summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--config.py3
-rwxr-xr-xcutter91
3 files changed, 75 insertions, 21 deletions
diff --git a/TODO b/TODO
index 474585f..910885b 100644
--- a/TODO
+++ b/TODO
@@ -4,4 +4,4 @@ OK 3. write tags to the splitted files
OK 4. specify output file format including path
OK 5. add support of config file with default options
OK 6. copy file instead of convert if possible
-7. substitute odd symbols in track names
+OK 7. substitute odd symbols in track names
diff --git a/config.py b/config.py
index 6345c7d..d24c418 100644
--- a/config.py
+++ b/config.py
@@ -50,4 +50,5 @@ SAMPLE_RATE = cfg.getint("output", "sample_rate")
CHANNELS = cfg.getint("output", "channels")
BITS_PER_SAMPLE = cfg.getint("output", "bits_per_sample")
-FILENAME_FORMAT = cfg.get("encoding", "format", DEFAULT_FILENAME_FORMAT)
+FILENAME_FORMAT = cfg.get("filename", "format", DEFAULT_FILENAME_FORMAT)
+CONVERT_CHARS = cfg.getbool("filename", "convert_chars", False)
diff --git a/cutter b/cutter
index 895b494..415fee1 100755
--- a/cutter
+++ b/cutter
@@ -13,6 +13,17 @@ import collections
import sys
import os
+ILLEGAL_CHARACTERS_MAP = {
+ u"\\": u"-",
+ u":": u"-",
+ u"*": u"+",
+ u"?": u"_",
+ u'"': u"'",
+ u"<": u"(",
+ u">": u")",
+ u"|": u"-"
+}
+
progname = os.path.basename(sys.argv[0])
def printf(fmt, *args):
@@ -36,7 +47,7 @@ except Exception as err:
printerr("import config failed: %s", err)
sys.exit(0)
-def quote(s, ch = "\""):
+def quote(s, ch = '"'):
return s if " " not in s else ch + s + ch
def msf(ts):
@@ -124,10 +135,6 @@ def parse_args():
enc = OptionGroup(parser, "Encoding options")
- enc.add_option("--format",
- dest="fmt", default=config.FILENAME_FORMAT,
- help="the format string for new filenames")
-
enc.add_option("-d", "--dir",
dest="dir", default=config.DIR, help="output directory")
@@ -137,6 +144,22 @@ def parse_args():
parser.add_option_group(enc)
+ fname = OptionGroup(parser, "Filename options")
+
+ fname.add_option("--format",
+ dest="fmt", default=config.FILENAME_FORMAT,
+ help="the format string for new filenames")
+
+ fname.add_option("--convert-chars",
+ dest="convert_chars", action="store_true",
+ help="replace illegal characters in filename")
+
+ fname.add_option("--no-convert-chars",
+ dest="convert_chars", action="store_false",
+ help="do not replace characters in filename")
+
+ parser.add_option_group(fname)
+
format = OptionGroup(parser, "Output format")
format.add_option("-r", "--sample-rate", type="int",
@@ -161,17 +184,27 @@ def parse_args():
return parser.parse_args()
-def verify_options(opt):
+def process_options(opt):
if opt.compression is not None and opt.compression < 0 or opt.compression > 8:
printerr("invalid compression value %d, must be in range 0 .. 8", opt.compression)
sys.exit(1)
- opt.dir = u"." if not opt.dir else to_unicode(opt.dir)
- opt.fmt = to_unicode(opt.fmt)
+ if not opt.dir:
+ opt.dir = u"."
+ else:
+ opt.dir = to_unicode(os.path.normpath(opt.dir))
+ opt.fmt = to_unicode(opt.fmt)
if not os.path.basename(opt.fmt):
printerr("invalid format option \"%s\"", opt.fmt)
sys.exit(1)
+ else:
+ opt.fmt = os.path.normpath(opt.fmt)
+ if opt.fmt.startswith("/"):
+ opt.fmt = opt.fmt[1:]
+
+ if opt.convert_chars is None:
+ opt.convert_chars = config.CONVERT_CHARS
def get_encoder_formart(opt, info):
cmd = "flac sox - "
@@ -196,6 +229,9 @@ def mkdir(path):
printerr("make dir %s failed: %s", quote(path), err)
sys.exit(1)
+def convert_characters(path):
+ return "".join([ILLEGAL_CHARACTERS_MAP.get(ch, ch) for ch in path])
+
class StreamInfo:
__mapping = {
b"Channels:": "channels",
@@ -238,6 +274,25 @@ class CueSplitter:
self.name = name
self.tags = tags
+ @staticmethod
+ def format_by_tags(fmt, tags, replace=False):
+ if replace:
+ def conv(var):
+ if isinstance(var, str):
+ return var.replace("/", "-")
+ return var
+
+ tags = {k: conv(v) for k, v in tags.items()}
+
+ try:
+ return fmt.format(year=tags["date"], **tags)
+ except KeyError as err:
+ printerr("invalid format key: %s", err)
+ sys.exit(1)
+ except ValueError as err:
+ printerr("invalid format option: %s", err)
+ sys.exit(1)
+
def __init__(self, cue, opt):
self.cue = cue
self.opt = opt
@@ -255,11 +310,10 @@ class CueSplitter:
"albumartist": self.opt.albumartist
}
- try:
- tmp = os.path.dirname(opt.fmt).format(**self.tags)
- except KeyError as err:
- printerr("invalid format key: %s", err)
- sys.exit(1)
+ tmp = self.format_by_tags(os.path.dirname(opt.fmt), self.tags, True)
+
+ if opt.convert_chars:
+ tmp = convert_characters(tmp)
self.dest = os.path.join(opt.dir, tmp)
track_fmt = os.path.basename(opt.fmt)
@@ -285,11 +339,10 @@ class CueSplitter:
or self.cue.get("songwriter")
})
- try:
- name = fmt.format(year=tags["date"], **tags)
- except KeyError as err:
- printerr("invalid format key: %s", err)
- sys.exit(1)
+ name = self.format_by_tags(fmt, tags).replace("/", "-")
+
+ if self.opt.convert_chars:
+ name = convert_characters(name)
return self.TrackInfo(name + "." + self.TRACK_SUFFIX, tags)
@@ -495,7 +548,7 @@ class CueSplitter:
def main():
options, args = parse_args()
- verify_options(options)
+ process_options(options)
if len(args) != 1:
printf("Usage: %s [options] cuefile\n", progname)