Source code for eyed3.plugins.statistics

# -*- coding: utf-8 -*-
################################################################################
#  Copyright (C) 2009  Travis Shirk <travis@pobox.com>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
################################################################################
from __future__ import print_function
import sys, os, operator
from collections import Counter

from eyed3 import id3
from eyed3.core import AUDIO_MP3
from eyed3.utils import guessMimetype, cli
from eyed3.plugins import LoaderPlugin

ID3_VERSIONS = [id3.ID3_V1_0, id3.ID3_V1_1,
                id3.ID3_V2_2, id3.ID3_V2_3, id3.ID3_V2_4]

_OP_STRINGS = {operator.le: "<=",
               operator.lt: "< ",
               operator.ge: ">=",
               operator.gt: "> ",
               operator.eq: "= ",
               operator.ne: "!=",
              }

[docs]class Stat(Counter): TOTAL = "total" def __init__(self, *args, **kwargs): super(Stat, self).__init__(*args, **kwargs) self[self.TOTAL] = 0 self._key_names = {}
[docs] def compute(self, file, audio_file): self[self.TOTAL] += 1 self._compute(file, audio_file)
def _compute(self, file, audio_file): pass
[docs] def report(self): self._report()
def _sortedKeys(self, most_common=False): def keyDisplayName(k): return self._key_names[k] if k in self._key_names else k key_map = {} for k in self.keys(): key_map[keyDisplayName(k)] = k if not most_common: sorted_names = list(key_map.keys()) sorted_names.remove(self.TOTAL) sorted_names.sort() sorted_names.append(self.TOTAL) else: most_common = self.most_common() sorted_names = [] remainder_names = [] for k, v in most_common: if k != self.TOTAL and v > 0: sorted_names.append(keyDisplayName(k)) elif k != self.TOTAL: remainder_names.append(keyDisplayName(k)) remainder_names.sort() sorted_names = sorted_names + remainder_names sorted_names.append(self.TOTAL) return [key_map[name] for name in sorted_names] def _report(self, most_common=False): keys = self._sortedKeys(most_common=most_common) key_col_width = 0 val_col_width = 0 for key in keys: key = self._key_names[key] if key in self._key_names else key key_col_width = max(key_col_width, len(str(key))) val_col_width = max(val_col_width, len(str(self[key]))) key_col_width += 1 val_col_width += 1 for k in keys: key_name = self._key_names[k] if k in self._key_names else k value = self[k] percent = self.percent(k) if value and k != "total" else "" print("%(padding)s%(key)s:%(value)s%(percent)s" % { "padding": ' ' * 4, "key": str(key_name).ljust(key_col_width), "value": str(value).rjust(val_col_width), "percent": " ( %s%.2f%%%s )" % (cli.GREEN, percent, cli.RESET) if percent else "", })
[docs] def percent(self, key): return (float(self[key]) / float(self["total"])) * 100
[docs]class AudioStat(Stat):
[docs] def compute(self, audio_file): assert(audio_file) self["total"] += 1 self._compute(audio_file)
def _compute(self, audio_file): pass
[docs]class FileCounterStat(Stat): def __init__(self): super(FileCounterStat, self).__init__() for k in ("audio", "hidden", "audio (other)"): self[k] = 0 def _compute(self, file, audio_file): if audio_file: self["audio"] += 1 if os.path.basename(file).startswith('.'): self["hidden"] += 1 mt = guessMimetype(file) if mt and mt.startswith("audio/") and not audio_file: self["unsupported (other)"] += 1 def _report(self): print(cli.BOLD + cli.GREY + "Files:" + cli.RESET) super(FileCounterStat, self)._report()
[docs]class MimeTypeStat(Stat): def _compute(self, file, audio_file): mt = guessMimetype(file) self[mt] += 1 def _report(self): print(cli.BOLD + cli.GREY + "Mime-Types:" + cli.RESET) super(MimeTypeStat, self)._report(most_common=True)
[docs]class Id3VersionCounter(AudioStat): def __init__(self): super(Id3VersionCounter, self).__init__() for v in ID3_VERSIONS: self[v] = 0 self._key_names[v] = id3.versionToString(v) def _compute(self, audio_file): if audio_file.tag: self[audio_file.tag.version] += 1 else: self[None] += 1 def _report(self): print(cli.BOLD + cli.GREY + "ID3 versions:" + cli.RESET) super(Id3VersionCounter, self)._report()
[docs]class BitrateCounter(AudioStat): def __init__(self): super(BitrateCounter, self).__init__() self["cbr"] = 0 self["vbr"] = 0 self.bitrate_keys = [(operator.le, 96), (operator.le, 112), (operator.le, 128), (operator.le, 160), (operator.le, 192), (operator.le, 256), (operator.le, 320), (operator.gt, 320), ] for k in self.bitrate_keys: self[k] = 0 op, bitrate = k self._key_names[k] = "%s %d" % (_OP_STRINGS[op], bitrate) def _compute(self, audio_file): if audio_file.type != AUDIO_MP3 or audio_file.info is None: self["total"] -=1 return vbr, br = audio_file.info.bit_rate if vbr: self["vbr"] += 1 else: self["cbr"] += 1 for key in self.bitrate_keys: key_op, key_br = key if key_op(br, key_br): self[key] += 1 break def _report(self): print(cli.BOLD + cli.GREY + "MP3 bitrates:" + cli.RESET) super(BitrateCounter, self)._report(most_common=True) def _sortedKeys(self, most_common=False): keys = super(BitrateCounter, self)._sortedKeys(most_common=most_common) keys.remove("cbr") keys.remove("vbr") keys.insert(0, "cbr") keys.insert(1, "vbr") return keys
[docs]class StatisticsPlugin(LoaderPlugin): NAMES = ['stats'] SUMMARY = u"Computes statistics for all audio files scanned." def __init__(self, arg_parser): super(StatisticsPlugin, self).__init__(arg_parser) self._stats = [] self.file_counter = FileCounterStat() self._stats.append(self.file_counter) self.mt_stat = MimeTypeStat() self._stats.append(self.mt_stat) self.id3_version_counter = Id3VersionCounter() self._stats.append(self.id3_version_counter) self.bitrates = BitrateCounter() self._stats.append(self.bitrates)
[docs] def handleFile(self, f): super(StatisticsPlugin, self).handleFile(f) sys.stdout.write('.') sys.stdout.flush() for stat in self._stats: if isinstance(stat, AudioStat): if self.audio_file: stat.compute(self.audio_file) else: stat.compute(f, self.audio_file)
[docs] def handleDone(self): print("\n") for stat in self._stats: stat.report() print("\n") print()