123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- import media.CDDB
- import time
- import subprocess
- from media import common
- import math
- from PIL import Image
- import os
- import logging
- import sys
-
-
- try:
- from config import APP_NAME as ROOT_LOGGER_NAME
- except ImportError:
- ROOT_LOGGER_NAME = 'root'
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
-
- # make module usable without discid dependency
- try:
- import discid
- except ModuleNotFoundError:
- logger.warning("Python module discid not installed")
- except OSError:
- logger.exception("You might install python3-libdiscid")
-
- __KEY_CAMERA_VENDOR__ = 'camera_vendor'
- __KEY_CAMERA_MODEL__ = 'camera_model'
-
-
- def get_media_data(full_path, user_callback=None):
- #
- ft = common.get_filetype(full_path)
- #
- if ft == common.FILETYPE_AUDIO:
- return get_audio_data(full_path)
- elif ft == common.FILETYPE_IMAGE:
- return get_image_data(full_path)
- elif ft == common.FILETYPE_VIDEO:
- return get_video_data(full_path)
- elif ft == common.FILETYPE_DISC:
- return get_disc_data(full_path, user_callback)
- else:
- logger.warning('Filetype not known: %s', full_path)
-
-
- def get_audio_data(full_path):
- conv_key_dict = {}
- conv_key_dict['album'] = (str, common.KEY_ALBUM)
- conv_key_dict['TAG:album'] = (str, common.KEY_ALBUM)
- conv_key_dict['TAG:artist'] = (str, common.KEY_ARTIST)
- conv_key_dict['artist'] = (str, common.KEY_ARTIST)
- conv_key_dict['bit_rate'] = (__int_conv__, common.KEY_BITRATE)
- conv_key_dict['duration'] = (float, common.KEY_DURATION)
- conv_key_dict['TAG:genre'] = (str, common.KEY_GENRE)
- conv_key_dict['genre'] = (str, common.KEY_GENRE)
- conv_key_dict['TAG:title'] = (str, common.KEY_TITLE)
- conv_key_dict['title'] = (str, common.KEY_TITLE)
- conv_key_dict['TAG:track'] = (__int_conv__, common.KEY_TRACK)
- conv_key_dict['track'] = (__int_conv__, common.KEY_TRACK)
- conv_key_dict['TAG:date'] = (__int_conv__, common.KEY_YEAR)
- conv_key_dict['date'] = (__int_conv__, common.KEY_YEAR)
- return __adapt__data__(__get_xxprobe_data__(full_path, conv_key_dict), full_path)
-
-
- def get_disc_data(full_path, user_callback):
- #
- # Read Information from CDDB database
- #
- did = media.CDDB.discid()
- if did is None:
- logger.error("Could not determine disc id...")
- sys.exit(1)
- q = media.CDDB.query(did)
- if q is None:
- data = {
- common.KEY_ARTIST: None,
- common.KEY_ALBUM: None,
- common.KEY_YEAR: None,
- common.KEY_GENRE: None
- }
- for i in range(0, int(did.split('+')[1])):
- data["track_%02d" % (i + 1)] = None
- data = user_callback(common.CALLBACK_MAN_INPUT, data)
- return media.CDDB.my_disc_metadata(**data)
-
- if len(q) == 1:
- # Only one entry
- did = tuple(q.keys())[0]
- else:
- # multiple entries (choose)
- if user_callback is None:
- logger.warning("No usercallback to handle multiple cddb choices...")
- sys.exit(1)
- did = user_callback(common.CALLBACK_CDDB_CHOICE, q)
-
- return media.CDDB.cddb(did)
- """
- musicbrainzngs.set_useragent("pyrip", "0.1", "your@mail")
- disc = discid.read()
- disc_id = disc.id
- disc_data = {}
- try:
- result = musicbrainzngs.get_releases_by_discid(disc_id, includes=["artists", "recordings"])
- except musicbrainzngs.ResponseError:
- logger.exception("disc not found or bad response")
- sys.exit(1)
- else:
- disc_data[common.KEY_ARTIST] = result["disc"]["release-list"][0]["artist-credit-phrase"]
- disc_data[common.KEY_ALBUM] = result["disc"]["release-list"][0]["title"]
- disc_data[common.KEY_YEAR] = int(result["disc"]["release-list"][0]["date"][:4])
- data_copy = dict(disc_data)
- disc_data["id"] = result["disc"]["release-list"][0]["id"]
- disc_data["tracks"] = []
- # get tracklist
- for entry in result["disc"]["release-list"][0]["medium-list"][0]["track-list"]:
- track = dict(data_copy)
- track[common.KEY_TITLE] = entry["recording"]["title"]
- track[common.KEY_TRACK] = int(entry['number'])
- disc_data["tracks"].append(track)
- return disc_data
- """
-
-
- def get_video_data(full_path):
- conv_key_dict = {}
- conv_key_dict['creation_time'] = (__vid_datetime_conv__, common.KEY_TIME)
- conv_key_dict['TAG:creation_time'] = (__vid_datetime_conv__, common.KEY_TIME)
- conv_key_dict['bit_rate'] = (__int_conv__, common.KEY_BITRATE)
- conv_key_dict['duration'] = (float, common.KEY_DURATION)
- conv_key_dict['height'] = (__int_conv__, common.KEY_HEIGHT)
- conv_key_dict['width'] = (__int_conv__, common.KEY_WIDTH)
- conv_key_dict['display_aspect_ratio'] = (__ratio_conv__, common.KEY_RATIO)
- return __adapt__data__(__get_xxprobe_data__(full_path, conv_key_dict), full_path)
-
-
- def get_image_data(full_path):
- return __adapt__data__(__get_exif_data__(full_path), full_path)
-
-
- def __adapt__data__(data, full_path):
- data[common.KEY_SIZE] = os.path.getsize(full_path)
- # Join Camera Vendor and Camera Model
- if __KEY_CAMERA_MODEL__ in data and __KEY_CAMERA_VENDOR__ in data:
- model = data.pop(__KEY_CAMERA_MODEL__)
- vendor = data.pop(__KEY_CAMERA_VENDOR__)
- data[common.KEY_CAMERA] = '%s: %s' % (vendor, model)
- # Add time if not exists
- if common.KEY_TIME not in data:
- if common.KEY_YEAR in data and common.KEY_TRACK in data:
- if data[common.KEY_YEAR] != 0: # ignore year 0 - must be wrong
- # Use a date where track 1 is the newest in the given year
- minute = int(data[common.KEY_TRACK] / 60)
- second = (data[common.KEY_TRACK] - 60 * minute) % 60
- #
- data[common.KEY_TIME] = int(time.mktime((data[common.KEY_YEAR], 1, 1, 0, 59 - minute, 59 - second, 0, 0, 0)))
- data[common.KEY_TIME_IS_SUBSTITUTION] = True
- else:
- data[common.KEY_TIME] = int(os.path.getmtime(full_path))
- data[common.KEY_TIME_IS_SUBSTITUTION] = True
- return data
-
-
- def __get_xxprobe_data__(full_path, conv_key_dict):
- def _ffprobe_command(full_path):
- return ['ffprobe', '-v', 'quiet', '-show_format', '-show_streams', full_path]
-
- def _avprobe_command(full_path):
- return ['avprobe', '-v', 'quiet', '-show_format', '-show_streams', full_path]
-
- try:
- xxprobe_text = subprocess.check_output(_avprobe_command(full_path))
- except FileNotFoundError:
- try:
- xxprobe_text = subprocess.check_output(_ffprobe_command(full_path))
- except FileNotFoundError:
- logger.warning('ffprobe and avprobe seem to be not installed')
- return {}
- #
- rv = {}
- for line in xxprobe_text.decode('utf-8').splitlines():
- try:
- key, val = [snippet.strip() for snippet in line.split('=')]
- except ValueError:
- continue
- else:
- if key in conv_key_dict:
- tp, name = conv_key_dict[key]
- try:
- rv[name] = tp(val)
- except ValueError:
- logger.log(logging.WARNING if val else logger.INFO, 'Can\'t convert %s (%s) for %s', repr(val), name, name)
- return rv
-
-
- def __get_exif_data__(full_path):
- rv = {}
- im = Image.open(full_path)
- try:
- exif = dict(im._getexif().items())
- except AttributeError:
- logger.debug('%s does not have any exif information', full_path)
- else:
- conv_key_dict = {}
- # IMAGE
- conv_key_dict[0x9003] = (__datetime_conv__, common.KEY_TIME)
- conv_key_dict[0x8822] = (__exposure_program_conv__, common.KEY_EXPOSURE_PROGRAM)
- conv_key_dict[0x829A] = (__num_denum_conv__, common.KEY_EXPOSURE_TIME)
- conv_key_dict[0x9209] = (__flash_conv__, common.KEY_FLASH)
- conv_key_dict[0x829D] = (__num_denum_conv__, common.KEY_APERTURE)
- conv_key_dict[0x920A] = (__num_denum_conv__, common.KEY_FOCAL_LENGTH)
- conv_key_dict[0x8825] = (__gps_conv__, common.KEY_GPS)
- conv_key_dict[0xA003] = (__int_conv__, common.KEY_HEIGHT)
- conv_key_dict[0x8827] = (__int_conv__, common.KEY_ISO)
- conv_key_dict[0x010F] = (str, __KEY_CAMERA_VENDOR__)
- conv_key_dict[0x0110] = (str, __KEY_CAMERA_MODEL__)
- conv_key_dict[0x0112] = (__int_conv__, common.KEY_ORIENTATION)
- conv_key_dict[0xA002] = (__int_conv__, common.KEY_WIDTH)
- for key in conv_key_dict:
- if key in exif:
- tp, name = conv_key_dict[key]
- raw_value = exif[key]
- logger.debug("Converting %s out of %s", name, repr(raw_value))
- value = tp(raw_value)
- if value is not None:
- rv[name] = value
- return rv
-
-
- # TODO: Join datetime converter __datetime_conv__ and __vid_datetime_conv_
- def __datetime_conv__(dt):
- format_string = "%Y:%m:%d %H:%M:%S"
- return int(time.mktime(time.strptime(dt, format_string)))
-
-
- def __vid_datetime_conv__(dt):
- try:
- dt = dt[:dt.index('.')]
- except ValueError:
- pass # time string seems to have no '.'
- dt = dt.replace('T', ' ').replace('/', '').replace('\\', '')
- if len(dt) == 16:
- dt += ':00'
- format_string = '%Y-%m-%d %H:%M:%S'
- return int(time.mktime(time.strptime(dt, format_string)))
-
-
- def __exposure_program_conv__(n):
- return {
- 0: 'Unidentified',
- 1: 'Manual',
- 2: 'Program Normal',
- 3: 'Aperture Priority',
- 4: 'Shutter Priority',
- 5: 'Program Creative',
- 6: 'Program Action',
- 7: 'Portrait Mode',
- 8: 'Landscape Mode'
- }.get(n, None)
-
-
- def __flash_conv__(n):
- return {
- 0: 'No',
- 1: 'Fired',
- 5: 'Fired (?)', # no return sensed
- 7: 'Fired (!)', # return sensed
- 9: 'Fill Fired',
- 13: 'Fill Fired (?)',
- 15: 'Fill Fired (!)',
- 16: 'Off',
- 24: 'Auto Off',
- 25: 'Auto Fired',
- 29: 'Auto Fired (?)',
- 31: 'Auto Fired (!)',
- 32: 'Not Available'
- }.get(n, None)
-
-
- def __int_conv__(value):
- try:
- return int(value)
- except ValueError:
- for c in ['.', '/', '-']:
- p = value.find(c)
- if p >= 0:
- value = value[:p]
- try:
- return int(value)
- except ValueError:
- return None
-
-
- def __num_denum_conv__(data):
- try:
- return float(data)
- except TypeError:
- num, denum = data
- return num / denum
-
-
- def __gps_conv__(data):
- def lat_lon_cal(lon_or_lat):
- lon_lat = 0.
- fac = 1.
- for data in lon_or_lat:
- try:
- lon_lat += float(data[0]) / float(data[1]) * fac
- except TypeError:
- lon_lat += data * fac
- except ZeroDivisionError:
- return 0.
- fac *= 1. / 60.
- if math.isnan(lon_lat):
- return 0.
- return lon_lat
- try:
- lon = lat_lon_cal(data[0x0004])
- lat = lat_lon_cal(data[0x0002])
- if lon != 0 or lat != 0: # do not use lon and lat equal 0, caused by motorola gps weakness
- return {'lon': lon, 'lat': lat}
- except KeyError:
- logger.warning('GPS data extraction failed for %s', repr(data))
-
-
- def __ratio_conv__(ratio):
- ratio = ratio.replace('\\', '')
- num, denum = ratio.split(':')
- return float(num) / float(denum)
|