mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-19 21:03:21 -07:00
Initial Commit
This commit is contained in:
commit
88daa3fb91
1311 changed files with 256240 additions and 0 deletions
284
lib/mutagen/easymp4.py
Normal file
284
lib/mutagen/easymp4.py
Normal file
|
@ -0,0 +1,284 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2009 Joe Wreschnig
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of version 2 of the GNU General Public License as
|
||||
# published by the Free Software Foundation.
|
||||
|
||||
from mutagen import Metadata
|
||||
from mutagen._util import DictMixin, dict_match
|
||||
from mutagen.mp4 import MP4, MP4Tags, error, delete
|
||||
from ._compat import PY2, text_type, PY3
|
||||
|
||||
|
||||
__all__ = ["EasyMP4Tags", "EasyMP4", "delete", "error"]
|
||||
|
||||
|
||||
class EasyMP4KeyError(error, KeyError, ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class EasyMP4Tags(DictMixin, Metadata):
|
||||
"""A file with MPEG-4 iTunes metadata.
|
||||
|
||||
Like Vorbis comments, EasyMP4Tags keys are case-insensitive ASCII
|
||||
strings, and values are a list of Unicode strings (and these lists
|
||||
are always of length 0 or 1).
|
||||
|
||||
If you need access to the full MP4 metadata feature set, you should use
|
||||
MP4, not EasyMP4.
|
||||
"""
|
||||
|
||||
Set = {}
|
||||
Get = {}
|
||||
Delete = {}
|
||||
List = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.__mp4 = MP4Tags(*args, **kwargs)
|
||||
self.load = self.__mp4.load
|
||||
self.save = self.__mp4.save
|
||||
self.delete = self.__mp4.delete
|
||||
|
||||
filename = property(lambda s: s.__mp4.filename,
|
||||
lambda s, fn: setattr(s.__mp4, 'filename', fn))
|
||||
|
||||
@classmethod
|
||||
def RegisterKey(cls, key,
|
||||
getter=None, setter=None, deleter=None, lister=None):
|
||||
"""Register a new key mapping.
|
||||
|
||||
A key mapping is four functions, a getter, setter, deleter,
|
||||
and lister. The key may be either a string or a glob pattern.
|
||||
|
||||
The getter, deleted, and lister receive an MP4Tags instance
|
||||
and the requested key name. The setter also receives the
|
||||
desired value, which will be a list of strings.
|
||||
|
||||
The getter, setter, and deleter are used to implement __getitem__,
|
||||
__setitem__, and __delitem__.
|
||||
|
||||
The lister is used to implement keys(). It should return a
|
||||
list of keys that are actually in the MP4 instance, provided
|
||||
by its associated getter.
|
||||
"""
|
||||
key = key.lower()
|
||||
if getter is not None:
|
||||
cls.Get[key] = getter
|
||||
if setter is not None:
|
||||
cls.Set[key] = setter
|
||||
if deleter is not None:
|
||||
cls.Delete[key] = deleter
|
||||
if lister is not None:
|
||||
cls.List[key] = lister
|
||||
|
||||
@classmethod
|
||||
def RegisterTextKey(cls, key, atomid):
|
||||
"""Register a text key.
|
||||
|
||||
If the key you need to register is a simple one-to-one mapping
|
||||
of MP4 atom name to EasyMP4Tags key, then you can use this
|
||||
function::
|
||||
|
||||
EasyMP4Tags.RegisterTextKey("artist", "\xa9ART")
|
||||
"""
|
||||
def getter(tags, key):
|
||||
return tags[atomid]
|
||||
|
||||
def setter(tags, key, value):
|
||||
tags[atomid] = value
|
||||
|
||||
def deleter(tags, key):
|
||||
del(tags[atomid])
|
||||
|
||||
cls.RegisterKey(key, getter, setter, deleter)
|
||||
|
||||
@classmethod
|
||||
def RegisterIntKey(cls, key, atomid, min_value=0, max_value=(2 ** 16) - 1):
|
||||
"""Register a scalar integer key.
|
||||
"""
|
||||
|
||||
def getter(tags, key):
|
||||
return list(map(text_type, tags[atomid]))
|
||||
|
||||
def setter(tags, key, value):
|
||||
clamp = lambda x: int(min(max(min_value, x), max_value))
|
||||
tags[atomid] = [clamp(v) for v in map(int, value)]
|
||||
|
||||
def deleter(tags, key):
|
||||
del(tags[atomid])
|
||||
|
||||
cls.RegisterKey(key, getter, setter, deleter)
|
||||
|
||||
@classmethod
|
||||
def RegisterIntPairKey(cls, key, atomid, min_value=0,
|
||||
max_value=(2 ** 16) - 1):
|
||||
def getter(tags, key):
|
||||
ret = []
|
||||
for (track, total) in tags[atomid]:
|
||||
if total:
|
||||
ret.append(u"%d/%d" % (track, total))
|
||||
else:
|
||||
ret.append(text_type(track))
|
||||
return ret
|
||||
|
||||
def setter(tags, key, value):
|
||||
clamp = lambda x: int(min(max(min_value, x), max_value))
|
||||
data = []
|
||||
for v in value:
|
||||
try:
|
||||
tracks, total = v.split("/")
|
||||
tracks = clamp(int(tracks))
|
||||
total = clamp(int(total))
|
||||
except (ValueError, TypeError):
|
||||
tracks = clamp(int(v))
|
||||
total = min_value
|
||||
data.append((tracks, total))
|
||||
tags[atomid] = data
|
||||
|
||||
def deleter(tags, key):
|
||||
del(tags[atomid])
|
||||
|
||||
cls.RegisterKey(key, getter, setter, deleter)
|
||||
|
||||
@classmethod
|
||||
def RegisterFreeformKey(cls, key, name, mean="com.apple.iTunes"):
|
||||
"""Register a text key.
|
||||
|
||||
If the key you need to register is a simple one-to-one mapping
|
||||
of MP4 freeform atom (----) and name to EasyMP4Tags key, then
|
||||
you can use this function::
|
||||
|
||||
EasyMP4Tags.RegisterFreeformKey(
|
||||
"musicbrainz_artistid", "MusicBrainz Artist Id")
|
||||
"""
|
||||
atomid = "----:" + mean + ":" + name
|
||||
|
||||
def getter(tags, key):
|
||||
return [s.decode("utf-8", "replace") for s in tags[atomid]]
|
||||
|
||||
def setter(tags, key, value):
|
||||
encoded = []
|
||||
for v in value:
|
||||
if not isinstance(v, text_type):
|
||||
if PY3:
|
||||
raise TypeError("%r not str" % v)
|
||||
v = v.decode("utf-8")
|
||||
encoded.append(v.encode("utf-8"))
|
||||
tags[atomid] = encoded
|
||||
|
||||
def deleter(tags, key):
|
||||
del(tags[atomid])
|
||||
|
||||
cls.RegisterKey(key, getter, setter, deleter)
|
||||
|
||||
def __getitem__(self, key):
|
||||
key = key.lower()
|
||||
func = dict_match(self.Get, key)
|
||||
if func is not None:
|
||||
return func(self.__mp4, key)
|
||||
else:
|
||||
raise EasyMP4KeyError("%r is not a valid key" % key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
key = key.lower()
|
||||
|
||||
if PY2:
|
||||
if isinstance(value, basestring):
|
||||
value = [value]
|
||||
else:
|
||||
if isinstance(value, text_type):
|
||||
value = [value]
|
||||
|
||||
func = dict_match(self.Set, key)
|
||||
if func is not None:
|
||||
return func(self.__mp4, key, value)
|
||||
else:
|
||||
raise EasyMP4KeyError("%r is not a valid key" % key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
key = key.lower()
|
||||
func = dict_match(self.Delete, key)
|
||||
if func is not None:
|
||||
return func(self.__mp4, key)
|
||||
else:
|
||||
raise EasyMP4KeyError("%r is not a valid key" % key)
|
||||
|
||||
def keys(self):
|
||||
keys = []
|
||||
for key in self.Get.keys():
|
||||
if key in self.List:
|
||||
keys.extend(self.List[key](self.__mp4, key))
|
||||
elif key in self:
|
||||
keys.append(key)
|
||||
return keys
|
||||
|
||||
def pprint(self):
|
||||
"""Print tag key=value pairs."""
|
||||
strings = []
|
||||
for key in sorted(self.keys()):
|
||||
values = self[key]
|
||||
for value in values:
|
||||
strings.append("%s=%s" % (key, value))
|
||||
return "\n".join(strings)
|
||||
|
||||
for atomid, key in {
|
||||
'\xa9nam': 'title',
|
||||
'\xa9alb': 'album',
|
||||
'\xa9ART': 'artist',
|
||||
'aART': 'albumartist',
|
||||
'\xa9day': 'date',
|
||||
'\xa9cmt': 'comment',
|
||||
'desc': 'description',
|
||||
'\xa9grp': 'grouping',
|
||||
'\xa9gen': 'genre',
|
||||
'cprt': 'copyright',
|
||||
'soal': 'albumsort',
|
||||
'soaa': 'albumartistsort',
|
||||
'soar': 'artistsort',
|
||||
'sonm': 'titlesort',
|
||||
'soco': 'composersort',
|
||||
}.items():
|
||||
EasyMP4Tags.RegisterTextKey(key, atomid)
|
||||
|
||||
for name, key in {
|
||||
'MusicBrainz Artist Id': 'musicbrainz_artistid',
|
||||
'MusicBrainz Track Id': 'musicbrainz_trackid',
|
||||
'MusicBrainz Album Id': 'musicbrainz_albumid',
|
||||
'MusicBrainz Album Artist Id': 'musicbrainz_albumartistid',
|
||||
'MusicIP PUID': 'musicip_puid',
|
||||
'MusicBrainz Album Status': 'musicbrainz_albumstatus',
|
||||
'MusicBrainz Album Type': 'musicbrainz_albumtype',
|
||||
'MusicBrainz Release Country': 'releasecountry',
|
||||
}.items():
|
||||
EasyMP4Tags.RegisterFreeformKey(key, name)
|
||||
|
||||
for name, key in {
|
||||
"tmpo": "bpm",
|
||||
}.items():
|
||||
EasyMP4Tags.RegisterIntKey(key, name)
|
||||
|
||||
for name, key in {
|
||||
"trkn": "tracknumber",
|
||||
"disk": "discnumber",
|
||||
}.items():
|
||||
EasyMP4Tags.RegisterIntPairKey(key, name)
|
||||
|
||||
|
||||
class EasyMP4(MP4):
|
||||
"""Like :class:`MP4 <mutagen.mp4.MP4>`,
|
||||
but uses :class:`EasyMP4Tags` for tags.
|
||||
|
||||
:ivar info: :class:`MP4Info <mutagen.mp4.MP4Info>`
|
||||
:ivar tags: :class:`EasyMP4Tags`
|
||||
"""
|
||||
|
||||
MP4Tags = EasyMP4Tags
|
||||
|
||||
Get = EasyMP4Tags.Get
|
||||
Set = EasyMP4Tags.Set
|
||||
Delete = EasyMP4Tags.Delete
|
||||
List = EasyMP4Tags.List
|
||||
RegisterTextKey = EasyMP4Tags.RegisterTextKey
|
||||
RegisterKey = EasyMP4Tags.RegisterKey
|
Loading…
Add table
Add a link
Reference in a new issue