Add logging capability (#175)

* Refactoring and addition of logzero (#172)

* Refactored convert.py and added logging.

* Added logging and refactored.

* Added logzero to requirements.txt

* Added logging to metadata.py

* Created a log in misc.py. Updated slugify import.

* Some general improvement

* Improve message layout

* Improve test mechanism

* Implement debug level logging

* Fix some minor mistakes

* Make pytest happy

* Remove unimplemented --verbose option

* Update ISSUE_TEMPLATE.md

* Rename LICENSE

* Remove obvious from log.debug()

* Show track URL when writing to file (debug)
This commit is contained in:
Ritiek Malhotra
2017-12-15 19:57:57 +05:30
committed by GitHub
parent 8ea89f9d1c
commit df513acc35
12 changed files with 319 additions and 216 deletions

View File

@@ -1,50 +1,54 @@
import subprocess
import os
from core.logger import log
"""
What are the differences and similarities between ffmpeg, libav, and avconv?
"""What are the differences and similarities between ffmpeg, libav, and avconv?
https://stackoverflow.com/questions/9477115
ffmeg encoders high to lower quality
libopus > libvorbis >= libfdk_aac > aac > libmp3lame
libfdk_aac due to copyrights needs to be compiled by end user
on MacOS brew install ffmpeg --with-fdk-aac will do just that. Other OS?
https://trac.ffmpeg.org/wiki/Encode/AAC
"""
def song(input_song, output_song, folder, avconv=False, verbose=False):
"""Do the audio format conversion."""
def song(input_song, output_song, folder, avconv=False):
""" Do the audio format conversion. """
if not input_song == output_song:
print('Converting {0} to {1}'.format(
log.info('Converting {0} to {1}'.format(
input_song, output_song.split('.')[-1]))
if avconv:
exit_code = convert_with_avconv(input_song, output_song, folder, verbose)
exit_code = convert_with_avconv(input_song, output_song, folder)
else:
exit_code = convert_with_ffmpeg(input_song, output_song, folder, verbose)
exit_code = convert_with_ffmpeg(input_song, output_song, folder)
return exit_code
return 0
def convert_with_avconv(input_song, output_song, folder, verbose):
"""Convert the audio file using avconv."""
if verbose:
def convert_with_avconv(input_song, output_song, folder):
""" Convert the audio file using avconv. """
if log.level == 10:
level = 'debug'
else:
level = '0'
command = ['avconv',
'-loglevel', level,
'-i', os.path.join(folder, input_song),
'-ab', '192k',
command = ['avconv', '-loglevel', level, '-i',
os.path.join(folder, input_song), '-ab', '192k',
os.path.join(folder, output_song)]
log.debug(command)
return subprocess.call(command)
def convert_with_ffmpeg(input_song, output_song, folder, verbose):
"""Convert the audio file using FFmpeg."""
def convert_with_ffmpeg(input_song, output_song, folder):
""" Convert the audio file using FFmpeg. """
ffmpeg_pre = 'ffmpeg -y '
if not verbose:
if not log.level == 10:
ffmpeg_pre += '-hide_banner -nostats -v panic '
input_ext = input_song.split('.')[-1]
@@ -63,6 +67,9 @@ def convert_with_ffmpeg(input_song, output_song, folder, verbose):
ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn '
command = '{0}-i {1} {2}{3}'.format(
ffmpeg_pre, os.path.join(folder, input_song), ffmpeg_params, os.path.join(folder, output_song)).split(' ')
ffmpeg_pre, os.path.join(folder, input_song),
ffmpeg_params, os.path.join(folder, output_song)).split(' ')
log.debug(command)
return subprocess.call(command)

View File

@@ -1,28 +1,31 @@
import sys
import os
import argparse
import spotipy.oauth2 as oauth2
from urllib.request import quote
from slugify import slugify
from slugify import SLUG_OK, slugify
import sys
import os
from core.logger import log, log_leveller, _LOG_LEVELS_STR
def input_link(links):
"""Let the user input a number."""
""" Let the user input a choice. """
while True:
try:
the_chosen_one = int(input('>> Choose your number: '))
log.info('Choose your number:')
the_chosen_one = int(input('> '))
if 1 <= the_chosen_one <= len(links):
return links[the_chosen_one - 1]
elif the_chosen_one == 0:
return None
else:
print('Choose a valid number!')
log.warning('Choose a valid number!')
except ValueError:
print('Choose a valid number!')
log.warning('Choose a valid number!')
def trim_song(file):
"""Remove the first song from file."""
""" Remove the first song from file. """
with open(file, 'r') as file_in:
data = file_in.read().splitlines(True)
with open(file, 'w') as file_out:
@@ -60,26 +63,32 @@ def get_arguments():
'-f', '--folder', default=(os.path.join(sys.path[0], 'Music')),
help='path to folder where files will be stored in')
parser.add_argument(
'-v', '--verbose', default=False, help='show debug output',
action='store_true')
parser.add_argument(
'-i', '--input_ext', default='.m4a',
'-i', '--input-ext', default='.m4a',
help='prefered input format .m4a or .webm (Opus)')
parser.add_argument(
'-o', '--output_ext', default='.mp3',
'-o', '--output-ext', default='.mp3',
help='prefered output extension .mp3 or .m4a (AAC)')
parser.add_argument(
'-ll', '--log-level', default='INFO',
choices=_LOG_LEVELS_STR,
type=str.upper,
help='possible values - {}'.format(_LOG_LEVELS_STR))
return parser.parse_args()
parsed = parser.parse_args()
parsed.log_level = log_leveller(parsed.log_level)
return parsed
def is_spotify(raw_song):
"""Check if the input song is a Spotify link."""
""" Check if the input song is a Spotify link. """
status = len(raw_song) == 22 and raw_song.replace(" ", "%20") == raw_song
status = status or raw_song.find('spotify') > -1
return status
def is_youtube(raw_song):
"""Check if the input song is a YouTube link."""
""" Check if the input song is a YouTube link. """
status = len(raw_song) == 11 and raw_song.replace(" ", "%20") == raw_song
status = status and not raw_song.lower() == raw_song
status = status or 'youtube.com/watch?v=' in raw_song
@@ -87,7 +96,7 @@ def is_youtube(raw_song):
def sanitize_title(title):
"""Generate filename of the song to be downloaded."""
""" Generate filename of the song to be downloaded. """
title = title.replace(' ', '_')
title = title.replace('/', '_')
@@ -97,7 +106,7 @@ def sanitize_title(title):
def generate_token():
"""Generate the token. Please respect these credentials :)"""
""" Generate the token. Please respect these credentials :) """
credentials = oauth2.SpotifyClientCredentials(
client_id='4fe3fecfe5334023a1472516cc99d805',
client_secret='0f02b7c483c04257984695007a4a8d5c')
@@ -106,7 +115,7 @@ def generate_token():
def generate_search_url(song, viewsort=False):
"""Generate YouTube search URL for the given song."""
""" Generate YouTube search URL for the given song. """
# urllib.request.quote() encodes URL with special characters
song = quote(song)
if viewsort:
@@ -125,18 +134,14 @@ def filter_path(path):
os.remove(os.path.join(path, temp))
def grace_quit():
print('\n\nExiting.')
sys.exit(0)
def get_sec(time_str):
v = time_str.split(':', 3)
v.reverse()
sec = 0
if len(v) > 0: #seconds
sec += int(v[0])
if len(v) > 1: # minutes
sec += int(v[1]) * 60
if len(v) > 2: # hours
sec += int(v[2]) * 3600
return sec
v = time_str.split(':', 3)
v.reverse()
sec = 0
if len(v) > 0: # seconds
sec += int(v[0])
if len(v) > 1: # minutes
sec += int(v[1]) * 60
if len(v) > 2: # hours
sec += int(v[2]) * 3600
return sec

16
core/logger.py Normal file
View File

@@ -0,0 +1,16 @@
import logzero
import logging
_LOG_LEVELS_STR = ['INFO', 'WARNING', 'ERROR', 'DEBUG']
def log_leveller(log_level_str):
loggin_levels = [logging.INFO, logging.WARNING, logging.ERROR, logging.DEBUG]
log_level_str_index = _LOG_LEVELS_STR.index(log_level_str)
loggin_level = loggin_levels[log_level_str_index]
return loggin_level
# Create a logger
log_format = ("%(color)s%(levelname)s:%(end_color)s %(message)s")
formatter = logzero.LogFormatter(fmt=log_format)
log = logzero.setup_logger(formatter=formatter, level=logging.INFO)

View File

@@ -1,6 +1,7 @@
from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3, APIC
from mutagen.mp4 import MP4, MP4Cover
from core.logger import log
import urllib.request
@@ -24,23 +25,23 @@ def compare(music_file, metadata):
def embed(music_file, meta_tags):
"""Embed metadata."""
""" Embed metadata. """
if meta_tags is None:
print('Could not find meta-tags')
log.warning('Could not find metadata')
return None
elif music_file.endswith('.m4a'):
print('Fixing meta-tags')
log.info('Applying metadata')
return embed_m4a(music_file, meta_tags)
elif music_file.endswith('.mp3'):
print('Fixing meta-tags')
log.info('Applying metadata')
return embed_mp3(music_file, meta_tags)
else:
print('Cannot embed meta-tags into given output extension')
log.warning('Cannot embed metadata into given output extension')
return False
def embed_mp3(music_file, meta_tags):
"""Embed metadata to MP3 files."""
""" Embed metadata to MP3 files. """
# EasyID3 is fun to use ;)
audiofile = EasyID3(music_file)
audiofile['artist'] = meta_tags['artists'][0]['name']
@@ -81,7 +82,7 @@ def embed_mp3(music_file, meta_tags):
def embed_m4a(music_file, meta_tags):
"""Embed metadata to M4A files."""
""" Embed metadata to M4A files. """
# Apple has specific tags - see mutagen docs -
# http://mutagen.readthedocs.io/en/latest/api/mp4.html
tags = {'album': '\xa9alb',