Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Finished pylint - code refactored and removed unnecessary classes
  • Loading branch information
Javinator9889 committed Oct 15, 2019
1 parent 6e6ade3 commit 2457dd7
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 129 deletions.
3 changes: 0 additions & 3 deletions YouTubeMDBot/decorators/decorators.py
Expand Up @@ -18,9 +18,6 @@
from ..constants import PROGRAM_ARGS


# logging = LoggingHandler()


def send_action(action):
"""
Sends an action while processing a command.
Expand Down
4 changes: 4 additions & 0 deletions YouTubeMDBot/errors/EmptyBodyError.py
Expand Up @@ -16,4 +16,8 @@


class EmptyBodyError(Exception):
"""
Raises an exception when the body of the json data is empty (e.g.: there is no
video information)
"""
pass
19 changes: 0 additions & 19 deletions YouTubeMDBot/errors/InvalidCredentialsError.py

This file was deleted.

20 changes: 0 additions & 20 deletions YouTubeMDBot/errors/NoMatchError.py

This file was deleted.

20 changes: 0 additions & 20 deletions YouTubeMDBot/errors/ProcessorError.py

This file was deleted.

2 changes: 0 additions & 2 deletions YouTubeMDBot/errors/__init__.py
Expand Up @@ -14,5 +14,3 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ..errors.EmptyBodyError import EmptyBodyError
from ..errors.NoMatchError import NoMatchError
from ..errors.ProcessorError import ProcessorError
54 changes: 51 additions & 3 deletions YouTubeMDBot/logging_utils/utils.py
Expand Up @@ -17,6 +17,10 @@


def cleanup_old_logs(log_file: str):
"""
Cleans-up the old log files.
:param log_file: log filename that must be cleaned.
"""
import tarfile
import os

Expand All @@ -33,6 +37,14 @@ def cleanup_old_logs(log_file: str):

def setup_logging(logger_name: str, log_file: str, level=logging.DEBUG,
formatter: str = "%(process)d - %(asctime)s | [%(levelname)s]: %(message)s"):
"""
Creates a new logging which can log to stdout or file.
:param logger_name: the logger name.
:param log_file: log filename.
:param level: logging level.
:param formatter: the logging formatter.
:return: the logging file handler.
"""
from os import path
from os import makedirs

Expand All @@ -53,8 +65,12 @@ def setup_logging(logger_name: str, log_file: str, level=logging.DEBUG,
return logging_file_handler


class LoggingHandler(object):
class __LoggingHandler(object):
class LoggingHandler:
"""
LoggingHandler singleton class that outputs to multiple logging instances.
"""

class __LoggingHandler:
def __init__(self, logs: list):
self.__logs = logs

Expand Down Expand Up @@ -84,10 +100,18 @@ def get_loggers(self) -> list:
__instance = None

def __new__(cls, *args, **kwargs):
"""
Generates a new instance.
:param args: not used.
:param kwargs: "logs" is a list instance that must be provided the first time
this class is created.
:return: the LoggingHandler instance.
"""
if not LoggingHandler.__instance:
logs = kwargs.get("logs")
if not logs or len(logs) == 0:
raise AttributeError("At least kwarg \"log\" (a list of the loggers) must be provided")
raise AttributeError(
"At least kwarg \"log\" (a list of the loggers) must be provided")
LoggingHandler.__instance = LoggingHandler.__LoggingHandler(logs)
return LoggingHandler.__instance

Expand All @@ -98,19 +122,43 @@ def __setattr__(self, key, value):
return setattr(self.__instance, key, value)

def debug(self, msg):
"""
Debugs to loggers
:param msg: message to debug
"""
self.__instance.debug(msg)

def info(self, msg):
"""
Info to loggers
:param msg: message to info
"""
self.__instance.info(msg)

def error(self, msg):
"""
Error to loggers
:param msg: message to error
"""
self.__instance.error(msg)

def warning(self, msg):
"""
Warns to loggers
:param msg: message to warn
"""
self.__instance.warning(msg)

def critical(self, msg):
"""
Critical to loggers
:param msg: message to critical
"""
self.__instance.critical(msg)

def get_loggers(self) -> list:
"""
Obtains the list of loggers.
:return: the list of loggers.
"""
return self.__instance.get_loggers()
33 changes: 33 additions & 0 deletions YouTubeMDBot/metadata/AudioMetadata.py
Expand Up @@ -19,27 +19,60 @@


class AudioMetadata:
"""
Wrapper class for setting the audio metadata to the downloaded YouTube video
object. By using this class, it is possible to set the required information by
using mutagen without having to remember the metadata keys.
"""
def __init__(self, audio: BytesIO):
"""
Generates a new instance.
:param audio: the audio metadata, in BytesIO, in MP4 format.
"""
self._audio = MP4(audio)
self._data = audio

def set_title(self, title: str):
"""
Sets the audio title.
:param title: the audio title.
"""
self._audio[u"\xa9nam"] = title

def set_artist(self, artist: str):
"""
Sets the audio artist.
:param artist: the audio artist.
"""
self._audio[u"\xa9ART"] = artist

def set_album(self, album: str):
"""
Sets the audio album.
:param album: the audio album
"""
self._audio[u"\xa9alb"] = album

def set_extras(self, extras: list):
"""
Sets the audio extras.
:param extras: a list of extras that will be added to the audio information.
"""
self._audio[u"\xa9cmt"] = '; '.join(map(str, extras))

def set_cover(self, cover: bytes):
"""
Sets the audio cover.
:param cover: the audio cover.
"""
mp4_cover = MP4Cover(cover, MP4Cover.FORMAT_JPEG)
self._audio[u"covr"] = [mp4_cover]

def save(self) -> BytesIO:
"""
Saves the new metadata into the audio file object.
:return: the audio file object with the new metadata.
"""
self._data.seek(0)
self._audio.save(self._data)
return self._data
70 changes: 65 additions & 5 deletions YouTubeMDBot/metadata/MetadataIdentifier.py
Expand Up @@ -13,13 +13,12 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import acoustid
import musicbrainzngs

try:
import ujson as json
except ImportError:
import json
import acoustid
import musicbrainzngs

from ..audio import FPCalc
from ..api import YouTubeAPI
Expand All @@ -28,8 +27,30 @@
from ..downloader import YouTubeDownloader


class MetadataIdentifier(object):
class MetadataIdentifier:
"""
Base identifier class. By using the audio data, calculates and generates a
fingerprint for searching across the MusicBrainz database.
Once the audio has been identified, the available params and information are:
- audio (bytes)
- result (json)
- artist (str)
- title (str)
- release_id (str)
- recording_id (str)
- score (float)
- cover (bytes)
- album (str)
- duration (int)
- youtube_data (bool)
- youtube_id (str)
"""
def __init__(self, audio: bytes):
"""
Generates a new instance of the MetadataIdentifier class.
:param audio: the audio data, in bytes.
"""
self.audio = audio
self.result: json = None
self.artist: str = ""
Expand All @@ -45,6 +66,12 @@ def __init__(self, audio: bytes):

@staticmethod
def _is_valid_result(data: json) -> bool:
"""
Checks whether the obtained result, in json, is valid or not, by checking for
certain keys that must exist.
:param data: the result in json.
:return: 'True' if the result is valid, else 'False'.
"""
if "results" not in data:
return False
elif data["status"] != "ok":
Expand All @@ -58,6 +85,12 @@ def _is_valid_result(data: json) -> bool:
return True

def identify_audio(self) -> bool:
"""
Tries to identify the audio by using the audio fingerprint. If the audio has
been successfully identified, then obtains all the data related to it.
:return: 'True' if the result is valid (the audio was correctly identified),
else 'False'.
"""
fingerprint = FPCalc(self.audio)
data: json = acoustid.lookup(apikey=ACOUSTID_KEY,
fingerprint=fingerprint.fingerprint(),
Expand Down Expand Up @@ -91,7 +124,35 @@ def identify_audio(self) -> bool:


class YouTubeMetadataIdentifier(MetadataIdentifier):
"""
Identifies YouTube metadata by using MusicBrainz database and YouTube metadata. If
the first identification (MusicBrainz) fails, then fallback to YouTube
identification if the "downloader" was provided.
Once the audio has been identified, the available params and information are:
- audio (bytes)
- result (json)
- artist (str)
- title (str)
- release_id (str)
- recording_id (str)
- score (float)
- cover (bytes)
- album (str)
- duration (int)
- youtube_data (bool)
- youtube_id (str)
If "youtube_data" is True, then only audio, title, artist, duration, cover and
youtube_id are available.
"""
def __init__(self, audio: bytes, downloader: YouTubeDownloader = None):
"""
Generates a new instance of the MetadataIdentifier class.
:param audio: the audio data, in bytes.
:param downloader: a downloader object, for obtaining the video information if
MusicBrainz fails.
"""
super().__init__(audio)
self._downloader = downloader

Expand All @@ -111,5 +172,4 @@ def identify_audio(self) -> bool:
self.youtube_data = True

valid = True

return valid
1 change: 0 additions & 1 deletion YouTubeMDBot/utils/__init__.py
Expand Up @@ -14,4 +14,3 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ..utils.youtube_utils import get_yt_video_id
from ..utils.timeout import timeout

0 comments on commit 2457dd7

Please sign in to comment.