Module musar.config
Wrapper for configurable options and parameters.
Expand source code
"""Wrapper for configurable options and parameters.
"""
import re
import logging
from . import accessors
from . import constraints
from . import scopes
from . import rules
from . import cleaners
from . import formats
from .misc import TextEditor
class Options:
"""Namespace for simple parameters.
Attributes
----------
cover_target_size : Tuple[int, int]
Size covers will be resized to in `musar.cleaners.Resize`.
cover_target_format : str
Image file format for the cover (in Pillow).
See `musar.accessors.Cover`.
cover_target_encoding : str
Color format for the cover (in Pillow).
See `musar.accessors.Cover`.
youtube_dl_path : str
Path to the *youtube-dl* executable.
mp3tag_path : str
Path to the *Mp3tag* executable.
download_folder : str
Root folder for downloaded files.
ffmpeg_path : str
Path to the *FFmpeg* executable.
"""
def __init__(self):
self.cover_target_size = 600, 600
self.cover_target_format = "jpeg"
self.cover_target_encoding = "RBG"
self.youtube_dl_path = "youtube-dl"
self.mp3tag_path = "Mp3tag.exe"
self.download_folder = "downloads"
self.ffmpeg_path = "ffmpeg"
def __str__(self):
return "\n".join([
"cover_target_size=%d,%d" % self.cover_target_size,
"cover_target_format=%s" % self.cover_target_format,
"cover_target_encoding=%s" % self.cover_target_encoding,
"youtube_dl_path=%s" % self.youtube_dl_path,
"mp3tag_path=%s" % self.mp3tag_path,
"download_folder=%s" % self.download_folder,
"ffmpeg_path=%s" % self.ffmpeg_path
])
def load(self, line):
"""Load a configuration file line.
Parameters
----------
line : str
Line to parse.
"""
split = line.strip().split("=")
if len(split) != 2:
return
key, value = tuple(map(lambda s: s.strip(), split))
if key not in self.__dict__:
return
if key == "cover_target_size":
value = tuple(map(lambda s: int(s.strip()), value.split(",")))
setattr(self, key, value)
class Config: # pylint: disable=R0902
"""Global configuration.
Attributes
----------
rules : List[musar.rules.Rule]
Rules that tracks should follow.
formats : List[musar.formats.Format]
Formats for cleaning the tag values.
options : musar.config.Options
General parameters.
extensions : Set[str]
Set of extensions for `musar.action_convert`.
accessor_mgr : musar.accessors.Manager
Accessors manager.
constraint_mgr : musar.constraints.Manager
Constraints manager.
scope_mgr : musar.scopes.Manager
Scopes manager.
cleaner_mgr : musar.cleaners.Manager
Cleaners manager.
"""
def __init__(self):
self.rules = list()
self.formats = list()
self.options = Options()
self.extensions = set()
self.accessor_mgr = accessors.Manager(self)
self.constraint_mgr = constraints.Manager()
self.scope_mgr = scopes.Manager()
self.cleaner_mgr = cleaners.Manager(self)
def __str__(self):
return "[RULES]\n" + "\n".join(map(str, self.rules))\
+ "\n\n[FORMATS]\n" + "\n".join(map(str, self.formats))\
+ "\n\n[OPTIONS]\n" + str(self.options)\
+ "\n\n[EXTENSIONS]\n" + "\n".join(self.extensions)
@classmethod
def from_file(cls, path):
"""Create a `Config` object from a text file.
Parameters
----------
cls : type
Class to create.
path : str
Path to the text file to parse.
Returns
-------
musar.config.Config
Created config
"""
config = Config()
config.load(path)
return config
def _load_rule(self, line):
split = line.strip().split(" ")
if len(split) != 3:
return
accessor_name, constraint_name, scope_name = split
if accessor_name not in self.accessor_mgr:
return
if constraint_name not in self.constraint_mgr:
return
if scope_name not in self.scope_mgr:
return
self.rules.append(rules.Rule(
self.accessor_mgr[accessor_name],
self.constraint_mgr[constraint_name],
self.scope_mgr[scope_name]
))
def _load_format(self, line):
split = line.strip().split(" ")
if len(split) == 0:
return
if split[0] not in self.accessor_mgr:
return
cleaners_ = list()
for cleaner_name in split[1:]:
if cleaner_name not in self.cleaner_mgr:
continue
cleaners_.append(self.cleaner_mgr[cleaner_name])
self.formats.append(formats.Format(
self.accessor_mgr[split[0]],
*cleaners_
))
def _load_extension(self, line):
self.extensions.add(line.strip())
def _load_text(self, text):
current_category = None
for line in text.split("\n"):
if line.strip() == "" or line.strip().startswith("#"):
continue
match = re.search(r"\[(.*?)\]", line)
if match is not None:
current_category = match.group(1)
continue
if current_category == "RULES":
self._load_rule(line)
elif current_category == "FORMATS":
self._load_format(line)
elif current_category == "OPTIONS":
self.options.load(line)
elif current_category == "EXTENSIONS":
self._load_extension(line)
def load(self, path):
"""Parse config from an external file.
Parameters
----------
path : str
Path to the external config file.
"""
logging.info("Parsing file %s for config %s", path, repr(self))
with open(path) as infile:
self._load_text(infile.read())
def reset(self):
"""Reset the accessors memories.
"""
logging.info("Resetting config %s", repr(self))
for accessor in self.accessor_mgr.values():
del accessor.memory
accessor.memory = dict()
def edit(self):
"""Prompt user with a text editor for live editing the current config.
This will not modify the actual file.
"""
logging.info("Editing config %s", repr(self))
current_config = self.__str__()
editor = TextEditor()
new_config = editor(current_config)
self.rules = list()
self.formats = list()
self.options = Options()
self.extensions = set()
self._load_text(new_config.strip())
Classes
class Config
-
Global configuration.
Attributes
rules
:List[Rule]
- Rules that tracks should follow.
formats
:List[Format]
- Formats for cleaning the tag values.
options
:Options
- General parameters.
extensions
:Set[str]
- Set of extensions for
action_convert()
. accessor_mgr
:Manager
- Accessors manager.
constraint_mgr
:Manager
- Constraints manager.
scope_mgr
:Manager
- Scopes manager.
cleaner_mgr
:Manager
- Cleaners manager.
Expand source code
class Config: # pylint: disable=R0902 """Global configuration. Attributes ---------- rules : List[musar.rules.Rule] Rules that tracks should follow. formats : List[musar.formats.Format] Formats for cleaning the tag values. options : musar.config.Options General parameters. extensions : Set[str] Set of extensions for `musar.action_convert`. accessor_mgr : musar.accessors.Manager Accessors manager. constraint_mgr : musar.constraints.Manager Constraints manager. scope_mgr : musar.scopes.Manager Scopes manager. cleaner_mgr : musar.cleaners.Manager Cleaners manager. """ def __init__(self): self.rules = list() self.formats = list() self.options = Options() self.extensions = set() self.accessor_mgr = accessors.Manager(self) self.constraint_mgr = constraints.Manager() self.scope_mgr = scopes.Manager() self.cleaner_mgr = cleaners.Manager(self) def __str__(self): return "[RULES]\n" + "\n".join(map(str, self.rules))\ + "\n\n[FORMATS]\n" + "\n".join(map(str, self.formats))\ + "\n\n[OPTIONS]\n" + str(self.options)\ + "\n\n[EXTENSIONS]\n" + "\n".join(self.extensions) @classmethod def from_file(cls, path): """Create a `Config` object from a text file. Parameters ---------- cls : type Class to create. path : str Path to the text file to parse. Returns ------- musar.config.Config Created config """ config = Config() config.load(path) return config def _load_rule(self, line): split = line.strip().split(" ") if len(split) != 3: return accessor_name, constraint_name, scope_name = split if accessor_name not in self.accessor_mgr: return if constraint_name not in self.constraint_mgr: return if scope_name not in self.scope_mgr: return self.rules.append(rules.Rule( self.accessor_mgr[accessor_name], self.constraint_mgr[constraint_name], self.scope_mgr[scope_name] )) def _load_format(self, line): split = line.strip().split(" ") if len(split) == 0: return if split[0] not in self.accessor_mgr: return cleaners_ = list() for cleaner_name in split[1:]: if cleaner_name not in self.cleaner_mgr: continue cleaners_.append(self.cleaner_mgr[cleaner_name]) self.formats.append(formats.Format( self.accessor_mgr[split[0]], *cleaners_ )) def _load_extension(self, line): self.extensions.add(line.strip()) def _load_text(self, text): current_category = None for line in text.split("\n"): if line.strip() == "" or line.strip().startswith("#"): continue match = re.search(r"\[(.*?)\]", line) if match is not None: current_category = match.group(1) continue if current_category == "RULES": self._load_rule(line) elif current_category == "FORMATS": self._load_format(line) elif current_category == "OPTIONS": self.options.load(line) elif current_category == "EXTENSIONS": self._load_extension(line) def load(self, path): """Parse config from an external file. Parameters ---------- path : str Path to the external config file. """ logging.info("Parsing file %s for config %s", path, repr(self)) with open(path) as infile: self._load_text(infile.read()) def reset(self): """Reset the accessors memories. """ logging.info("Resetting config %s", repr(self)) for accessor in self.accessor_mgr.values(): del accessor.memory accessor.memory = dict() def edit(self): """Prompt user with a text editor for live editing the current config. This will not modify the actual file. """ logging.info("Editing config %s", repr(self)) current_config = self.__str__() editor = TextEditor() new_config = editor(current_config) self.rules = list() self.formats = list() self.options = Options() self.extensions = set() self._load_text(new_config.strip())
Static methods
def from_file(path)
-
Create a
Config
object from a text file.Parameters
cls
:type
- Class to create.
path
:str
- Path to the text file to parse.
Returns
Config
- Created config
Expand source code
@classmethod def from_file(cls, path): """Create a `Config` object from a text file. Parameters ---------- cls : type Class to create. path : str Path to the text file to parse. Returns ------- musar.config.Config Created config """ config = Config() config.load(path) return config
Methods
def edit(self)
-
Prompt user with a text editor for live editing the current config. This will not modify the actual file.
Expand source code
def edit(self): """Prompt user with a text editor for live editing the current config. This will not modify the actual file. """ logging.info("Editing config %s", repr(self)) current_config = self.__str__() editor = TextEditor() new_config = editor(current_config) self.rules = list() self.formats = list() self.options = Options() self.extensions = set() self._load_text(new_config.strip())
def load(self, path)
-
Parse config from an external file.
Parameters
path
:str
- Path to the external config file.
Expand source code
def load(self, path): """Parse config from an external file. Parameters ---------- path : str Path to the external config file. """ logging.info("Parsing file %s for config %s", path, repr(self)) with open(path) as infile: self._load_text(infile.read())
def reset(self)
-
Reset the accessors memories.
Expand source code
def reset(self): """Reset the accessors memories. """ logging.info("Resetting config %s", repr(self)) for accessor in self.accessor_mgr.values(): del accessor.memory accessor.memory = dict()
class Options
-
Namespace for simple parameters.
Attributes
cover_target_size
:Tuple[int, int]
- Size covers will be resized to in
Resize
. cover_target_format
:str
- Image file format for the cover (in Pillow).
See
Cover
. cover_target_encoding
:str
- Color format for the cover (in Pillow).
See
Cover
. youtube_dl_path
:str
- Path to the youtube-dl executable.
mp3tag_path
:str
- Path to the Mp3tag executable.
download_folder
:str
- Root folder for downloaded files.
ffmpeg_path
:str
- Path to the FFmpeg executable.
Expand source code
class Options: """Namespace for simple parameters. Attributes ---------- cover_target_size : Tuple[int, int] Size covers will be resized to in `musar.cleaners.Resize`. cover_target_format : str Image file format for the cover (in Pillow). See `musar.accessors.Cover`. cover_target_encoding : str Color format for the cover (in Pillow). See `musar.accessors.Cover`. youtube_dl_path : str Path to the *youtube-dl* executable. mp3tag_path : str Path to the *Mp3tag* executable. download_folder : str Root folder for downloaded files. ffmpeg_path : str Path to the *FFmpeg* executable. """ def __init__(self): self.cover_target_size = 600, 600 self.cover_target_format = "jpeg" self.cover_target_encoding = "RBG" self.youtube_dl_path = "youtube-dl" self.mp3tag_path = "Mp3tag.exe" self.download_folder = "downloads" self.ffmpeg_path = "ffmpeg" def __str__(self): return "\n".join([ "cover_target_size=%d,%d" % self.cover_target_size, "cover_target_format=%s" % self.cover_target_format, "cover_target_encoding=%s" % self.cover_target_encoding, "youtube_dl_path=%s" % self.youtube_dl_path, "mp3tag_path=%s" % self.mp3tag_path, "download_folder=%s" % self.download_folder, "ffmpeg_path=%s" % self.ffmpeg_path ]) def load(self, line): """Load a configuration file line. Parameters ---------- line : str Line to parse. """ split = line.strip().split("=") if len(split) != 2: return key, value = tuple(map(lambda s: s.strip(), split)) if key not in self.__dict__: return if key == "cover_target_size": value = tuple(map(lambda s: int(s.strip()), value.split(","))) setattr(self, key, value)
Methods
def load(self, line)
-
Load a configuration file line.
Parameters
line
:str
- Line to parse.
Expand source code
def load(self, line): """Load a configuration file line. Parameters ---------- line : str Line to parse. """ split = line.strip().split("=") if len(split) != 2: return key, value = tuple(map(lambda s: s.strip(), split)) if key not in self.__dict__: return if key == "cover_target_size": value = tuple(map(lambda s: int(s.strip()), value.split(","))) setattr(self, key, value)