Exemplo n.º 1
0
 def extract_xmp(file_path, open_forupdate=False):
     """
         This method use xmp lib to extra xmp metadata
     :param file_path: path
     :param open_forupdate: False by default
     :return: unicode string representation of xmp metadata
     """
     xmpfile = libxmp.XMPFiles(file_path=file_path,
                               open_forupdate=open_forupdate)
     libxmp.XMPFiles()
     xmp = xmpfile.get_xmp()
     xmpfile.close_file()
     return xmp.serialize_to_unicode()
Exemplo n.º 2
0
def apply_keyword_local(pair):

    if 'Full Collection' in pair.album:
        pair.keyword = pair.album.lstrip('Full Collection')
    elif 'Album' in pair.album:
        pair.keyword = pair.album.lstrip('Album')
    else:
        pair.keyword = pair.album
    try:
        xmpfile = libxmp.XMPFiles(file_path=os.path.join(
            pair.local_path, pair.local_fn),
                                  open_forupdate=True)
        xmp = xmpfile.get_xmp()
        #Using the dict, you can get a full dump of all file contens:
        #        xmpdict = libxmp.utils.object_to_dict(xmp)
        current_keywords = xmp.get_property(libxmp.consts.XMP_NS_DC,
                                            'subject[1]')
        if not pair.keyword in current_keywords:  #See: http://purl.org/dc/elements/1.1/ in the dict or online
            if not current_keywords[-1] == ',' and len(current_keywords) > 0:
                pair.keyword = ',' + pair.keyword
            xmp.set_property(libxmp.consts.XMP_NS_DC, 'subject[1]',
                             current_keywords + pair.keyword)
            if xmpfile.can_put_xmp(xmp):
                xmpfile.put_xmp(xmp)
        xmpfile.close_file()
    except:
        pass
    return pair
Exemplo n.º 3
0
def process(fullpath, config, rcontext, columns=None):
    # Try to parse the GIF data
    try:
        image = Image.open(fullpath, "r")

        assorted = [
            image.info['version'],
            image.info['duration'],
            image.info['transparency'],
            image.info['background'],
        ]

        # Seek until EOF to find the number of animation frames
        noframes = 0
        try:
            while True:
                noframes += 1
                image.seek(image.tell() + 1)
        except EOFError:
            pass
        assorted.append(noframes)

        if 'loop' in image.info:
            assorted.append(image.info['loop'])
        else:
            assorted.append(0)

        if 'extension' in image.info:
            assorted.append(image.info['extension'])
        else:
            assorted.append(None)

        # Close the PIL image handler
        del image

        # Store the embedded XMP metadata
        if config.ENABLEXMP:
            import libxmp
            xmpfile = libxmp.XMPFiles(file_path=fullpath)
            assorted.append(str(xmpfile.get_xmp()))
            xmpfile.close_file()
        else:
            assorted.append(None)

        # Make sure we stored exactly the same amount of columns as
        # specified!!
        assert len(assorted) == len(columns)

        # Print some data that is stored in the database if debug is true
        if config.DEBUG:
            print "\nGIF file data:"
            for i in range(0, len(assorted)):
                print "%-18s %s" % (columns[i], assorted[i])
            print

        return assorted

    except:
        traceback.print_exc(file=sys.stderr)
        return None
Exemplo n.º 4
0
def process(fullpath, config, rcontext, columns=None):

        # Try to parse TGA data
        try:
            image = Image.open(fullpath, "r")

            assorted = [image.tile]
            info_dictionary = image.info

            # Check if compression is in info dictionary,
            # if so put it in our list
            if "compression" in info_dictionary:
                assorted.append(info_dictionary["compression"])
                info_dictionary.pop("compression")
            else:
                assorted.append(None)

            # Check if orientation is in info dictionary,
            # if so put it in our list
            if "orientation" in info_dictionary:
                assorted.append(info_dictionary["orientation"])
                info_dictionary.pop("orientation")
            else:
                assorted.append(None)

            # If there are still other values in the
            # dict then put those in column
            assorted.append(info_dictionary)

            # Delete variable
            del image, info_dictionary

            # Store the embedded XMP metadata
            if config.ENABLEXMP:
                import libxmp
                xmpfile = libxmp.XMPFiles(file_path=fullpath)
                assorted.append(str(xmpfile.get_xmp()))
                xmpfile.close_file()
            else:
                assorted.append(None)

            # Make sure we stored exactly the same amount of columns as
            # specified!!
            assert len(assorted) == len(columns)

            # Print some data that is stored in the database if debug is true
            if config.DEBUG:
                print "\nTGA file data:"
                for i in range(0, len(assorted)):
                    print "%-18s %s" % (columns[i], assorted[i])
                print

            return assorted

        except:
            traceback.print_exc(file=sys.stderr)

            # Store values in database so not the whole application crashes
            return None
Exemplo n.º 5
0
def get_dc_from_file(filename):
    '''Get an embedded XMP from a media file (PDF, JPEG, …) and extract the
	Dublin Core elements.
	
	Keyword arguments:
	filename -- name of the media file
	'''
    xmpfile = libxmp.XMPFiles(file_path=filename)
    xmp = xmpfile.get_xmp()
    return {} if type(xmp) is NoneType else __get_dc(xmp)
Exemplo n.º 6
0
    def sync_iptc(self, file_path: Path) -> None:
        """
        Sync metadata to file.

        Take in a file path because there's no guarantee file is at
        self.get_file_save_path()

        Note to future self: it doesn't matter if you put a namespace in the
        name like "xmp:CreateDate" vs "CreateDate" or "dc:creator" vs "creator"
        """
        if self.file_ext not in ("jpg", "jpeg", "png"):
            return

        xmpfile = libxmp.XMPFiles(file_path=str(file_path),
                                  open_forupdate=True)
        xmp = xmpfile.get_xmp()
        if not xmp:
            # TODO why do some PNG files not return xmp?
            return

        # Existing meta, this isn't very useful on it's own
        # xmpdict = libxmp.utils.object_to_dict(xmp)

        # Set simple metadata fields
        # https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#date-created
        try:
            xmp.get_property(XMP_NS_XMP, "CreateDate")
        except libxmp.XMPError:
            xmp.set_property(XMP_NS_XMP, "CreateDate", self.created_at)

        try:
            xmp.get_property(XMP_NS_XMP, "ModifyDate")
        except libxmp.XMPError:
            xmp.set_property(XMP_NS_XMP, "ModifyDate", self.updated_at)

        try:
            xmp.get_property(XMP_NS_XMP, "Rating")
        except libxmp.XMPError:
            # If we bookmarked it, we must like it, so set a default of "4"
            xmp.set_property(XMP_NS_XMP, "Rating", "4")

        # https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#creator
        add_array_xmp(xmp, "creator", self.artists)

        # https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#keywords
        add_array_xmp(
            xmp,
            "subject",
            self.copyright + self.characters +
            self.tag_string_general.split(" "),
        )

        if xmpfile.can_put_xmp(xmp):
            xmpfile.put_xmp(xmp)
        xmpfile.close_file()
Exemplo n.º 7
0
 def read_xmp_metadata(self, file):
     # Lecture du fichier
     xmpfile = libxmp.XMPFiles(file_path=file, open_forupdate=False)
     # Récupérer le XML
     xmp = str(xmpfile.get_xmp())
     xmp = "<rdf:RDF" + xmp.split("<rdf:RDF", 1)[1]
     xmp = xmp.split("</rdf:RDF>", 1)[0] + "</rdf:RDF>"
     # Fermer le fichier
     xmpfile.close_file()
     # Retourner le contenu RDF
     return xmp
Exemplo n.º 8
0
def embed_xmp_bytes(image: io.BytesIO, work_properties):
    """
    Given a file-like `io.BytesIO` object, embed ccREL metadata inside of it.
    For our purposes, we assume that the file is an image.

    :param image: A BytesIO representation of an image.
    :param work_properties: A dictionary with keys 'license_url' and
    'attribution'. 'creator', and 'work_landing_page' are optional (but highly
    recommended)
    :return: An `io.BytesIO` object containing XMP metadata.
    """

    # libxmp only works with actual file locations on the disk. To work around
    # this limitation, rather than embedding the metadata directly into the
    # `io.BytesIO` object, we have to use a temporary file and then convert it
    # back.
    # https://github.com/python-xmp-toolkit/python-xmp-toolkit/issues/46
    filename = '/tmp/{}'.format(uuid.uuid4())
    with open(filename, 'w+b') as xmp_temp:
        xmp_temp.write(image.getvalue())
        xmp_temp.flush()
        xmpfile = libxmp.XMPFiles(file_path=xmp_temp.name, open_forupdate=True)

        # Set CC rights.
        xmp = xmpfile.get_xmp()
        xmp.register_namespace(XMP_NS_CC, 'cc')
        xmp.set_property(XMP_NS_CC, 'license', work_properties['license_url'])
        if 'creator' in work_properties:
            if not xmp.does_property_exist(XMP_NS_CC, 'attributionName'):
                xmp.set_property(XMP_NS_CC, 'attributionName',
                                 work_properties['creator'])
        if 'work_landing_page' in work_properties:
            if not xmp.does_property_exist(XMP_NS_CC, 'attributionURL'):
                xmp.set_property(XMP_NS_CC, 'attributionURL',
                                 work_properties['work_landing_page'])
        xmp.register_namespace(XMP_NS_XMP, 'xmp')
        if 'identifier' in work_properties:
            if not xmp.does_property_exist(XMP_NS_XMP, 'Identifier'):
                xmp.set_property(XMP_NS_XMP, 'Identifier',
                                 work_properties['identifier'])
        # Set generic XMP rights.
        xmp.register_namespace(XMP_NS_XMP_Rights, 'xmpRights')
        if not xmp.does_property_exist(XMP_NS_XMP_Rights, 'XMP_NS_XMP_Rights'):
            xmp.set_property_bool(XMP_NS_XMP_Rights, 'Marked', True)
        if not xmp.does_property_exist(XMP_NS_XMP_Rights, 'UsageTerms'):
            usage = work_properties['attribution']
            xmp.set_property(XMP_NS_XMP_Rights, 'UsageTerms', usage)
        xmpfile.put_xmp(xmp)
        xmpfile.close_file()

    with open(filename, 'r+b') as xmpfile:
        file_with_xmp = io.BytesIO(xmpfile.read())
    os.remove(filename)
    return file_with_xmp
Exemplo n.º 9
0
def put_xmp_to_file(xmp_meta, file_path):
    xmp_file = libxmp.XMPFiles(file_path=file_path, open_forupdate=True)

    if not xmp_file.can_put_xmp(xmp_meta):
        if not file_format_supported(file_path):
            raise Exception("Image format of %s does not allow metadata" %
                            (file_path))
        else:
            raise Exception("Could not add metadata to image %s" % (file_path))

    xmp_file.put_xmp(xmp_meta)
    xmp_file.close_file()
Exemplo n.º 10
0
def wav_to_mp3(wavfn, delete_wav=False, lame_quality="V 2"):
    mp3fn = ".".join(wavfn.split('.')[:-1]) + '.mp3'
    check_output('lame -{} "{}"'.format(lame_quality, wavfn), shell=True)

    if LIBXMP:
        xmpfile = libxmp.XMPFiles(file_path=wavfn)
        xmpfile2 = libxmp.XMPFiles(file_path=mp3fn, open_forupdate=True)

        xmp = xmpfile.get_xmp()

        try:
            if xmpfile2.can_put_xmp(xmp):
                xmpfile2.put_xmp(xmp)
            else:
                print "Could not convert xmp from wav to mp3"
        except:
            print "File has no xmp data"

        xmpfile.close_file()
        xmpfile2.close_file()

    if delete_wav:
        check_output('rm "{}"'.format(wavfn), shell=True)
Exemplo n.º 11
0
    def open(self):
        if not self.is_open:
            # Open file for updating
            self.xmpfile = libxmp.XMPFiles(file_path=self.filename,
                                           open_forupdate=True)

            # Try to read existing XMP
            self.xmp = self.xmpfile.get_xmp()

            # Make new XMP if not exist
            if self.xmp is None:
                self.xmp = libxmp.core.XMPMeta()

            self.is_open = True

            # Get tags
            temp = self.get_tags()
Exemplo n.º 12
0
def process(file, config, rcontext, columns=None):
    fullpath = file.fullpath
    try:
        # open the wave file
        wave_file = wave.open(fullpath, "rb")

        # fill variables from the wave file, (nchannels, sampwidth,
        # framerate, nframes, comptype, compname)
        assorted = list(wave_file.getparams())

        # duration of the wave file is amount of frames divided by framerate
        assorted.append(wave_file.getnframes() /
                        float(wave_file.getframerate()))

        # close the wavefile first before opening using the XMP toolkit
        wave_file.close()

        # Store the embedded XMP metadata
        if config.ENABLEXMP:
            import libxmp
            xmpfile = libxmp.XMPFiles(file_path=fullpath)
            assorted.append(str(xmpfile.get_xmp()))
            xmpfile.close_file()
        else:
            assorted.append(None)

        # Make sure we stored exactly the same amount of columns as
        # specified!!
        assert len(assorted) == len(columns)

        # Print some data that is stored in the database if debug is true
        if config.DEBUG:
            print "\nWAV file data:"
            for i in range(0, len(assorted)):
                print "%-18s %s" % (columns[i], assorted[i])
            print

        return assorted

    except:
        traceback.print_exc(file=sys.stderr)

        return None
Exemplo n.º 13
0
def metadataToFile(result, fileDict):
    for row in result:
        # TODO: Check for existing file in fileDict. If not existing, KeyError will be raised as well.
        try:
            path = fileDict[row['Dateiname']]
            print("------------------------------")
            print(path)
            xmpfile = libxmp.XMPFiles(file_path=path, open_forupdate=True)
            xmp = xmpfile.get_xmp()

            # Edit each of the xmp attributes of the image
            for key, value in mapping.mapping.items():
                try:
                    if row[key] != "":
                        value_split = value.strip().split(':', 1)
                        # if option -o: the original xmp data will be deleted
                        if overrideMD == 1:
                            xmp.delete_property(
                                eval(value_split[0]),
                                unicode(value_split[1], 'utf-8'))

                        xmp.set_property(eval(value_split[0]),
                                         unicode(value_split[1], 'utf-8'),
                                         unicode(row[key], 'utf-8'))
                except libxmp.XMPError:
                    errorMessage = 'Attribute ' + value.upper(
                    ) + ' already existing for file: ' + row['Dateiname']
                    print errorMessage
                    logging.debug(errorMessage)
            # Save xmp metadata to the file
            xmpfile.put_xmp(xmp)
            xmpfile.close_file()

        except KeyError:
            errorMessage = 'KeyError found, while processing ' + row[
                'Dateiname'] + '. Wrong mapping.py settings or missing File?'
            print errorMessage
            logging.debug(errorMessage)
Exemplo n.º 14
0
def process(file, config, rcontext, columns=None):
    fullpath = file.fullpath

    # Try to parse PNG data
    try:
        image = Image.open(fullpath, "r")

        assorted = [image.tile, image.text]
        info_dictionary = image.info

        # Check if ICC profile is in info dictionary
        # if so put it in our list
        if "icc_profile" in info_dictionary:
            assorted.append(info_dictionary["icc_profile"])
            info_dictionary.pop("icc_profile")
        else:
            assorted.append(None)

        # Check if interlace is in info dictionary
        # if so put it in our list
        if "interlace" in info_dictionary:
            assorted.append(info_dictionary["interlace"])
            info_dictionary.pop("interlace")
        else:
            assorted.append(None)

        # Check if transparency is in info dictionary
        # if so put it in our list
        if "transparency" in info_dictionary:
            assorted.append(info_dictionary["transparency"])
            info_dictionary.pop("transparency")
        else:
            assorted.append(None)

        # Check if gamma is in info dictionary, if so put it in our list
        if "gamma" in info_dictionary:
            assorted.append(info_dictionary["gamma"])
            info_dictionary.pop("gamma")
        else:
            assorted.append(None)

        # Check if dpi is in info dictionary, if so put it in our list
        if "dpi" in info_dictionary:
            assorted.append(info_dictionary["dpi"][0])
            assorted.append(info_dictionary["dpi"][1])
            info_dictionary.pop("dpi")
        else:
            assorted.append(None)
            assorted.append(None)

        # Check if aspect is in info dictionary, if so put it in our list
        if "aspect" in image.info:
            assorted.append(info_dictionary["aspect"])
            info_dictionary.pop("aspect")
        else:
            assorted.append(None)

        # If there are still other values in the dict
        # then put those in column
        assorted.append(info_dictionary)

        # Delete variable
        del info_dictionary, image

        # Store the embedded XMP metadata
        if config.ENABLEXMP:
            import libxmp
            xmpfile = libxmp.XMPFiles(file_path=fullpath)
            assorted.append(str(xmpfile.get_xmp()))
            xmpfile.close_file()
        else:
            assorted.append(None)

        # Make sure we stored exactly the same amount of columns as
        # specified!!
        assert len(assorted) == len(columns)

        # Print some data that is stored in the database if debug is true
        if config.DEBUG:
            print "\nPNG file data:"
            for i in range(0, len(assorted)):
                print "%-18s %s" % (columns[i], assorted[i])
            print

        return assorted

    except:
        traceback.print_exc(file=sys.stderr)

        # Store values in database so not the whole application crashes
        return None
Exemplo n.º 15
0
import base64
import io
import libxmp
import logging
import zipfile

parser = argparse.ArgumentParser(
    description='Extract files from image metadata')
parser.add_argument('image',            help='Image with embedded files in metadata')

def extract_payload(payload):
    payload_io = io.BytesIO(base64.b64decode(payload))
    with zipfile.ZipFile(
            payload_io, mode='r', compression=zipfile.ZIP_LZMA) as zip_file:
        for filename in zip_file.namelist():
            logging.info(f'Extracting {filename}')
            zip_file.extract(filename)

if __name__ == '__main__':
    args = parser.parse_args()
    logging.basicConfig(level=logging.DEBUG)

    xmp_file = libxmp.XMPFiles(file_path=args.image)
    xmp_meta = xmp_file.get_xmp()

    if xmp_meta is None:
        print(f'Image {args.image} does not contain any embedded files')

    extract_payload(xmp_meta.get_property(
         libxmp.consts.XMP_NS_DC, u'embedded_payload'))
Exemplo n.º 16
0
def process(fullpath, config, rcontext, columns=None):

    # The whole parse data method is in a try block to catch any exception
    try:
        # Read the audio file and get the two data
        # classes from it (Tag and AudioInfo)
        track = eyed3.load(fullpath)
        track_tag = track.tag
        track_info = track.info

        # A variable to store mutiple strings in a list
        list_of_strings = []

        # Init list
        assorted = []

        # Store the title in the list
        assorted.append(track_tag.title)
        '''
        To get a tag from the audio file we need to set a frame
        in the eyed3 lib. In return we get mutiple frames or NoneType.
        For each frame we get the correct variable and this is stored
        in a list. After the loop we parse all found data into one string,
        this string for example look like: subtitle / subtitle / subtitle

        *The eyed3 lib says that it will never occur that there are
        multiple frames, that is why we store all data in one string,
        but to be sure of this, we still irritate through all frames*

        Because a NoneType can not be irritated we use the "or",
        so when we get a NoneType the for loop uses an empty list.
        Wich will result in an empty string because
        nothing is added to the list.
        '''
        # Store the subtitle in the list
        for subtitle_frame in track_tag.frame_set["TIT3"] or []:
            list_of_strings.append(subtitle_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the artist in the list
        assorted.append(track_tag.artist)

        # Store the album artist in the list
        list_of_strings = []
        for album_artist_frame in track_tag.frame_set["TPE2"] or []:
            list_of_strings.append(album_artist_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the album in the list
        assorted.append(track_tag.album)
        '''
        Some tags return an array or list of items.
        To get the correct data of these lists we first need to know
        if the list is not a NoneType or empty.
        If we don't check this an empty list will result in an exception,
        "[0]" can not be used an such list

        If the list is empty we still need to give something in return
        to the database, so we first define the variable to None.
        '''
        # Store the track_number in the list
        track_number = None
        track_total = None
        if (track_tag.track_num):
            track_number = track_tag.track_num[0]
            track_total = track_tag.track_num[1]
        assorted.append(track_number)
        assorted.append(track_total)

        # Store the disc_number in the list
        disc_number = None
        disc_total = None
        if (track_tag.disc_num):
            disc_number = track_tag.disc_num[0]
            disc_total = track_tag.disc_num[1]
        assorted.append(disc_number)
        assorted.append(disc_total)

        # delete variables
        del track_number, track_total, disc_number, disc_total

        # Store the cd_id in the list
        assorted.append(track_tag.cd_id)

        # Store the publisher in the list
        assorted.append(track_tag.publisher)

        # Store the composer in the list
        list_of_strings = []
        for composer_frame in track_tag.frame_set["TCOM"] or []:
            list_of_strings.append(composer_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the conductor in the list
        list_of_strings = []
        for conductor_frame in track_tag.frame_set["TPE3"] or []:
            list_of_strings.append(conductor_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the group content in the list
        list_of_strings = []
        for group_content_frame in track_tag.frame_set["TIT1"] or []:
            list_of_strings.append(group_content_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the releasedate and the recording year in the list
        assorted.append(track_tag.release_date)
        assorted.append(track_tag.recording_date)

        # Store beats per minute in the list
        assorted.append(track_tag.bpm)

        # Store the duration of the song in the list
        assorted.append(track_info.time_secs)

        # Store the play count of the song in the list
        assorted.append(track_tag.play_count)

        # Store the terms of use in the list
        assorted.append(track_tag.terms_of_use)

        # Store the language in the list
        list_of_strings = []
        for language_frame in track_tag.frame_set["TLAN"] or []:
            list_of_strings.append(language_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the rating in the list
        rating = 0
        for rating in track_tag.popularities:
            rating = rating.rating
        assorted.append(rating)

        # Store the genre in the list
        genre = None
        if (track_tag.genre):
            genre = track_tag.genre.name
        assorted.append(genre)

        # Store the comment data in the list
        comment_description = None
        comment_lang = None
        comment_text = None
        for comment in track_tag.comments:
            comment_description = comment.description
            comment_lang = comment.lang
            comment_text = comment.text
        assorted.append(comment_text)
        assorted.append(comment_description)
        assorted.append(comment_lang)

        # delete variables
        del rating, genre, comment_description, comment_lang, comment_text

        # Store the encoded by in the list
        list_of_strings = []
        for encoded_by_frame in track_tag.frame_set["TENC"] or []:
            list_of_strings.append(encoded_by_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the copyright in the list
        list_of_strings = []
        for copyright_frame in track_tag.frame_set["TCOP"] or []:
            list_of_strings.append(copyright_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the mood in the list
        list_of_strings = []
        for mood_frame in track_tag.frame_set["TMOO"] or []:
            list_of_strings.append(mood_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the compilation in the list
        list_of_strings = []
        for compitation_frame in track_tag.frame_set["TPE4"] or []:
            list_of_strings.append(compitation_frame.text)
        assorted.append(' / '.join(list_of_strings))

        # Store the user text data in the list
        user_text_description = None
        user_text_text = None
        for user_text in track_tag._user_texts:
            user_text_description = user_text.description
            user_text_text = user_text.text
        assorted.append(user_text_text)
        assorted.append(user_text_description)

        # Store the lyrics data in the list
        lyrics_description = None
        lyrics_text = None
        lyrics_lang = None
        for lyric in track_tag.lyrics:
            lyrics_description = lyric.description
            lyrics_text = lyric.text
            lyrics_lang = lyric.lang
        assorted.append(lyrics_text)
        assorted.append(lyrics_description)
        assorted.append(lyrics_lang)

        # Store the image data in the list
        image_description = None
        image_url = None
        image_picturetype = None
        for image in track_tag.images:
            image_description = image.description
            image_url = image.image_url
            image_picturetype = image.picTypeToString(image.picture_type)
        assorted.append(image_description)
        assorted.append(image_picturetype)
        assorted.append(image_url)

        # delete variables
        del user_text_description, user_text_text, lyrics_description
        del lyrics_lang, lyrics_text, image_description, image_url
        del image_picturetype

        # Store the chapter data in the list
        chapter_title = None
        chapter_subtitle = None
        chapter_starttime = None
        chapter_endtime = None
        chapter_startoffset = None
        chapter_endoffset = None
        for chapter in track_tag.chapters:
            chapter_title = chapter.title
            chapter_subtitle = chapter.subtitle
            if (chapter.times):
                chapter_starttime = chapter.times[0]
                chapter_endtime = chapter.times[1]
            if (chapter.offsets):
                chapter_startoffset = chapter.offsets[0]
                chapter_endoffset = chapter.offsets[1]
        assorted.append(chapter_title)
        assorted.append(chapter_subtitle)
        assorted.append(chapter_starttime)
        assorted.append(chapter_endtime)
        assorted.append(chapter_startoffset)
        assorted.append(chapter_endoffset)

        # delete variables
        del chapter_title, chapter_subtitle, chapter_starttime
        del chapter_endtime, chapter_startoffset, chapter_endoffset

        # Store all URL's in the list
        assorted.append(track_tag.commercial_url)
        assorted.append(track_tag.copyright_url)
        assorted.append(track_tag.artist_url)
        assorted.append(track_tag.audio_file_url)
        assorted.append(track_tag.audio_source_url)
        assorted.append(track_tag.internet_radio_url)
        assorted.append(track_tag.payment_url)
        assorted.append(track_tag.publisher_url)

        user_url = None
        for user_url_tag in track_tag._user_urls:
            user_url = user_url_tag.url
        assorted.append(user_url)

        # Delete all variables that are not used anymore
        del user_url, list_of_strings, track_info, track_tag, track

        # Get APEv2 tag from MP3,
        # because we don't know the key:value we store all data in one column
        try:
            apev2_tag = APEv2(fullpath)
        except:
            apev2_tag = None
        assorted.append(apev2_tag)

        # Delete variable
        del apev2_tag

        # Store the embedded XMP metadata
        if config.ENABLEXMP:
            import libxmp
            xmpfile = libxmp.XMPFiles(file_path=fullpath)
            assorted.append(str(xmpfile.get_xmp()))
            xmpfile.close_file()
        else:
            assorted.append(None)

        # Make sure we stored exactly the same amount of columns as
        # specified!!
        assert len(assorted) == len(columns)

        # Print some data that is stored in the database if debug is true
        if config.DEBUG:
            print "\nMPEG file data:"
            for i in range(0, len(assorted)):
                print "%-18s %s" % (columns[i], assorted[i])
            print

        # Store in database
        return assorted

    except:
        traceback.print_exc(file=sys.stderr)
        return None
Exemplo n.º 17
0
    def export(self, **kwargs):
        """
        Generate audio file from composition.

        :param str. filename: Output filename (no extension)
        :param str. filetype: Output file type (only .wav supported for now)
        :param integer samplerate: Sample rate of output audio
        :param integer channels: Channels in output audio, if different than originally specified
        :param bool. separate_tracks: Also generate audio file for each track in composition
        :param int min_length: Minimum length of output array (in frames). Will zero pad extra length.
        :param bool. adjust_dynamics: Automatically adjust dynamics (will document later)

        """
        # get optional args
        filename = kwargs.pop('filename', 'out')
        filetype = kwargs.pop('filetype', 'wav')
        adjust_dynamics = kwargs.pop('adjust_dynamics', False)
        samplerate = kwargs.pop('samplerate', None)
        channels = kwargs.pop('channels', self.channels)
        separate_tracks = kwargs.pop('separate_tracks', False)
        min_length = kwargs.pop('min_length', None)

        if samplerate is None:
            samplerate = np.min([track.samplerate for track in self.tracks])

        encoding = 'pcm16'
        to_mp3 = False
        if filetype == 'ogg':
            encoding = 'vorbis'
        elif filetype == 'mp3':
            filetype = 'wav'
            to_mp3 = True

        if separate_tracks:
            # build the separate parts of the composition if desired
            for track in self.tracks:
                out = self.build(track_list=[track],
                                 adjust_dynamics=adjust_dynamics,
                                 min_length=min_length,
                                 channels=channels)
                out_file = Sndfile(
                    "%s-%s.%s" % (filename, track.name, filetype), 'w',
                    Format(filetype, encoding=encoding), channels, samplerate)
                out_file.write_frames(out)
                out_file.close()

        # always build the complete composition
        out = self.build(adjust_dynamics=adjust_dynamics,
                         min_length=min_length,
                         channels=channels)

        out_filename = "%s.%s" % (filename, filetype)
        out_file = Sndfile(out_filename, 'w',
                           Format(filetype, encoding=encoding), channels,
                           samplerate)
        out_file.write_frames(out)
        out_file.close()

        if LIBXMP and filetype == "wav":
            xmp = libxmp.XMPMeta()
            ns = libxmp.consts.XMP_NS_DM
            p = xmp.get_prefix_for_namespace(ns)
            xpath = p + 'Tracks'
            xmp.append_array_item(ns,
                                  xpath,
                                  None,
                                  array_options={"prop_value_is_array": True},
                                  prop_value_is_struct=True)

            xpath += '[1]/' + p
            xmp.set_property(ns, xpath + "trackName", "CuePoint Markers")
            xmp.set_property(ns, xpath + "trackType", "Cue")
            xmp.set_property(ns, xpath + "frameRate", "f%d" % samplerate)

            for i, lab in enumerate(self.labels):
                xmp.append_array_item(
                    ns,
                    xpath + "markers",
                    None,
                    array_options={"prop_value_is_array": True},
                    prop_value_is_struct=True)
                xmp.set_property(ns, xpath + "markers[%d]/%sname" % (i + 1, p),
                                 lab.name)
                xmp.set_property(
                    ns, xpath + "markers[%d]/%sstartTime" % (i + 1, p),
                    str(lab.sample(samplerate)))

            xmpfile = libxmp.XMPFiles(file_path=out_filename,
                                      open_forupdate=True)
            if xmpfile.can_put_xmp(xmp):
                xmpfile.put_xmp(xmp)
            xmpfile.close_file()

        if to_mp3:
            wav_to_mp3(out_filename, delete_wav=True)

        return out
Exemplo n.º 18
0

# Returns base64-encoded ZIP archive with all files in file listing
def generate_payload(file_listing):
    zip_buffer = io.BytesIO()
    with zipfile.ZipFile(zip_buffer, mode='w',
                         compression=zipfile.ZIP_LZMA) as zip_file:
        for filename in file_listing:
            logging.info(f'Compressing file {filename}')
            zip_file.write(filename, os.path.basename(filename))
    return base64.b64encode(zip_buffer.getbuffer()).decode('utf8')


if __name__ == '__main__':
    args = parser.parse_args()
    logging.basicConfig(level=logging.DEBUG)

    # Create a copy of the input file
    with open(args.output, 'wb') as output_io:
        output_io.write(open(args.input, 'rb').read())

    logging.info(f'Reading input image {args.input}')
    xmp_file = libxmp.XMPFiles(file_path=args.output, open_forupdate=True)
    xmp_meta = libxmp.XMPMeta()
    xmp_meta.set_property(libxmp.consts.XMP_NS_DC, u'embedded_payload',
                          generate_payload(args.filename))

    logging.info(f'Writing output image {args.output}')
    xmp_file.put_xmp(xmp_meta)
    xmp_file.close_file()
Exemplo n.º 19
0
def main():
    # Program options management
    parser = OptionParser(
        usage="%prog imageFile [options]",
        version="0.1",
        description=
        "This tool generates multi-resolution astronomical images from large image for display by Stellarium/VirGO. The passed image is split in small tiles and the corresponding index JSON files are generated. Everything is output in the current directory.",
        formatter=IndentedHelpFormatter(max_help_position=33, width=80))
    parser.add_option("-f",
                      "--fitsheader",
                      dest="fitsHeader",
                      help="use the FITS header from FILE to get WCS info",
                      metavar="FILE")
    parser.add_option("-g",
                      "--gzipcompress",
                      dest="gzipCompress",
                      action="store_true",
                      default=False,
                      help="compress the produced JSON index using gzip")
    parser.add_option(
        "-t",
        "--tilesize",
        dest="tileSize",
        type="int",
        default=256,
        help="output image tiles size in pixel (default: %default)",
        metavar="SIZE")
    parser.add_option("-i",
                      "--onlyindex",
                      dest="makeImageTiles",
                      action="store_false",
                      default=True,
                      help="output only the JSON index")
    parser.add_option(
        "-l",
        "--maxLevPerIndex",
        dest="maxLevelPerIndex",
        default=3,
        type="int",
        help="put up to MAX levels per index file (default: %default)",
        metavar="MAX")
    parser.add_option(
        "-b",
        "--maxBrightness",
        dest="maxBrightness",
        default=13.,
        type="float",
        help=
        "the surface brightness of a white pixel of the image in V mag/arcmin^2 (default: %default)",
        metavar="MAG")
    parser.add_option("-a",
                      "--alphaBlend",
                      dest="alphaBlend",
                      action="store_true",
                      default=False,
                      help="activate alpha blending for this image")
    parser.add_option("--imgInfoShort",
                      dest="imgInfoShort",
                      type="string",
                      help="the short name of the image",
                      metavar="STR")
    parser.add_option("--imgInfoFull",
                      dest="imgInfoFull",
                      type="string",
                      help="the full name of the image",
                      metavar="STR")
    parser.add_option("--imgInfoUrl",
                      dest="imgInfoUrl",
                      type="string",
                      help="the info URL about the image",
                      metavar="STR")
    parser.add_option("--imgCreditsShort",
                      dest="imgCreditsShort",
                      type="string",
                      help="the short name of the image creator",
                      metavar="STR")
    parser.add_option("--imgCreditsFull",
                      dest="imgCreditsFull",
                      type="string",
                      help="the full name of the image creator",
                      metavar="STR")
    parser.add_option("--imgCreditsInfoUrl",
                      dest="imgCreditsInfoUrl",
                      type="string",
                      help="the info URL about the image creator",
                      metavar="STR")
    parser.add_option("--srvCreditsShort",
                      dest="serverCreditsShort",
                      type="string",
                      help="the short name of the hosting server",
                      metavar="STR")
    parser.add_option("--srvCreditsFull",
                      dest="serverCreditsFull",
                      type="string",
                      help="the full name of the hosting server",
                      metavar="STR")
    parser.add_option("--srvCreditsInfoUrl",
                      dest="serverCreditsInfoUrl",
                      type="string",
                      help="the info URL about the hosting server",
                      metavar="STR")
    (options, args) = parser.parse_args()

    # Need at least an image file as input
    if len(args) < 1:
        print "Usage: " + os.path.basename(
            sys.argv[0]) + " imageFile [options]"
        exit(0)
    imgFile = sys.argv[1]

    # We now have valid arguments
    if options.fitsHeader != None:
        # Try to read the provided FITS header file to extract the WCS
        wcs = astWCS.WCS(options.fitsHeader)
    else:
        # Else try to generate the WCS from the xmp informations contained in the file header
        import libxmp
        print "Try to import WCS info from the XMP headers in the image"
        libxmp.XMPFiles.initialize()
        xmpfile = libxmp.XMPFiles()
        xmpfile.open_file(imgFile, libxmp.consts.XMP_OPEN_READ)
        xmp = xmpfile.get_xmp()
        ns = 'http://www.communicatingastronomy.org/avm/1.0/'
        nxpix = int(
            xmp.get_array_item(ns, 'Spatial.ReferenceDimension', 1).keys()[0])
        nypix = int(
            xmp.get_array_item(ns, 'Spatial.ReferenceDimension', 2).keys()[0])
        ctype1 = 'RA---TAN'
        ctype2 = 'DEC--TAN'
        crpix1 = float(
            xmp.get_array_item(ns, 'Spatial.ReferencePixel', 1).keys()[0])
        crpix2 = float(
            xmp.get_array_item(ns, 'Spatial.ReferencePixel', 2).keys()[0])
        crval1 = float(
            xmp.get_array_item(ns, 'Spatial.ReferenceValue', 1).keys()[0])
        crval2 = float(
            xmp.get_array_item(ns, 'Spatial.ReferenceValue', 2).keys()[0])
        cdelt1 = float(xmp.get_array_item(ns, 'Spatial.Scale', 1).keys()[0])
        cdelt2 = float(xmp.get_array_item(ns, 'Spatial.Scale', 2).keys()[0])
        crota = float(xmp.get_property(ns, "Spatial.Rotation"))
        equinox = xmp.get_property(ns, "Spatial.Equinox")
        if equinox == 'J2000':
            equinox = 2000
        elif equinox == 'B1950':
            equinox = 1950
        else:
            equinox = float(equinox)
        coordFrameCstr = xmp.get_property(ns, "Spatial.CoordinateFrame")

        header = astWCS.pyfits.Header()
        header.update('SIMPLE', 'T')
        header.update('BITPIX', 8)
        header.update('NAXIS', 2)
        header.update('NAXIS1', nxpix)
        header.update('NAXIS2', nypix)
        header.update('CDELT1', cdelt1)
        header.update('CDELT2', cdelt2)
        header.update('CTYPE1', 'RA---TAN')
        header.update('CTYPE2', 'DEC--TAN')
        header.update('CRVAL1', crval1)
        header.update('CRVAL2', crval2)
        header.update('CRPIX1', crpix1)
        header.update('CRPIX2', crpix2)
        header.update('CROTA', crota)
        header.update('EQUINOX', equinox)
        header.update('RADECSYS', coordFrameCstr)
        wcs = astWCS.WCS(header, mode='pyfits')

    im = Image.open(imgFile)

    nbTileX = (im.size[0] + options.tileSize) // options.tileSize
    nbTileY = (im.size[1] + options.tileSize) // options.tileSize
    maxSize = max(im.size[0], im.size[1])
    nbLevels = 0
    while 2**nbLevels * options.tileSize < maxSize:
        nbLevels += 1
    print "Will tesselate image (", im.size[0], "x", im.size[
        1], ") in", nbTileX, 'x', nbTileY, 'tiles on', nbLevels + 1, 'levels'

    # Create the master level 0 tile, which recursively creates the subtiles
    masterTile = createTile(0, nbLevels, 0, 0, wcs, im, options.makeImageTiles,
                            options.tileSize)

    # Add some top-level informations
    masterTile.maxBrightness = options.maxBrightness
    masterTile.alphaBlend = options.alphaBlend
    masterTile.imageInfo.short = options.imgInfoShort
    masterTile.imageInfo.full = options.imgInfoFull
    masterTile.imageInfo.infoUrl = options.imgInfoUrl
    masterTile.imageCredits.short = options.imgCreditsShort
    masterTile.imageCredits.full = options.imgCreditsFull
    masterTile.imageCredits.infoUrl = options.imgCreditsInfoUrl
    masterTile.serverCredits.short = options.serverCreditsShort
    masterTile.serverCredits.full = options.serverCreditsFull
    masterTile.serverCredits.infoUrl = options.serverCreditsInfoUrl

    # masterTile contains the whole tree, just need to output it as desired
    masterTile.outputJSON(qCompress=options.gzipCompress,
                          maxLevelPerFile=options.maxLevelPerIndex)
Exemplo n.º 20
0
def get_xmp_from_file(file_path):
    return libxmp.XMPFiles(file_path=file_path).get_xmp()
Exemplo n.º 21
0
def process(fullpath, config, rcontext, columns=None):

    # Try to parse TIFF data
    try:
        image = Image.open(fullpath, "r")

        assorted = [image.tile]
        info_dictionary = image.info

        # Check if ICC profile is in info dictionary,
        # if so put it in our list
        if "icc_profile" in info_dictionary:
            assorted.append(info_dictionary["icc_profile"])
            info_dictionary.pop("icc_profile")
        else:
            assorted.append(None)

        # Check if compression is in info dictionary,
        # if so put it in our list
        if "compression" in info_dictionary:
            assorted.append(info_dictionary["compression"])
            info_dictionary.pop("compression")
        else:
            assorted.append(None)

        # Check if dpi is in info dictionary, if so put it in our list
        if "dpi" in info_dictionary:
            assorted.append(info_dictionary["dpi"][0])
            assorted.append(info_dictionary["dpi"][1])
            info_dictionary.pop("dpi")
        else:
            assorted.append(None)
            assorted.append(None)

        # Check if resolution is in info dictionary,
        # if so put it in our list
        if "resolution" in info_dictionary:
            assorted.append(info_dictionary["resolution"][0])
            assorted.append(info_dictionary["resolution"][1])
            info_dictionary.pop("resolution")
        else:
            assorted.append(None)
            assorted.append(None)

        # If there are still other values in the
        # dict then put those in column
        assorted.append(info_dictionary)

        # Get all values from tag atribute, EXIF Tags
        assorted.append(image.tag.get(TiffImagePlugin.BITSPERSAMPLE))
        assorted.append(
            image.tag.get(TiffImagePlugin.PHOTOMETRIC_INTERPRETATION))
        assorted.append(image.tag.get(TiffImagePlugin.FILLORDER))
        assorted.append(image.tag.get(TiffImagePlugin.IMAGEDESCRIPTION))
        assorted.append(image.tag.get(TiffImagePlugin.STRIPOFFSETS))
        assorted.append(image.tag.get(TiffImagePlugin.SAMPLESPERPIXEL))
        assorted.append(image.tag.get(TiffImagePlugin.ROWSPERSTRIP))
        assorted.append(image.tag.get(TiffImagePlugin.STRIPBYTECOUNTS))
        assorted.append(image.tag.get(TiffImagePlugin.X_RESOLUTION))
        assorted.append(image.tag.get(TiffImagePlugin.Y_RESOLUTION))
        assorted.append(image.tag.get(TiffImagePlugin.PLANAR_CONFIGURATION))
        assorted.append(image.tag.get(TiffImagePlugin.RESOLUTION_UNIT))
        assorted.append(image.tag.get(TiffImagePlugin.SOFTWARE))
        assorted.append(image.tag.get(TiffImagePlugin.DATE_TIME))
        assorted.append(image.tag.get(TiffImagePlugin.ARTIST))
        assorted.append(image.tag.get(TiffImagePlugin.PREDICTOR))
        assorted.append(image.tag.get(TiffImagePlugin.COLORMAP))
        assorted.append(image.tag.get(TiffImagePlugin.TILEOFFSETS))
        assorted.append(image.tag.get(TiffImagePlugin.EXTRASAMPLES))
        assorted.append(image.tag.get(TiffImagePlugin.SAMPLEFORMAT))
        assorted.append(image.tag.get(TiffImagePlugin.JPEGTABLES))
        assorted.append(image.tag.get(TiffImagePlugin.COPYRIGHT))
        assorted.append(image.tag.get(TiffImagePlugin.IPTC_NAA_CHUNK))
        assorted.append(image.tag.get(TiffImagePlugin.PHOTOSHOP_CHUNK))
        assorted.append(image.tag.get(TiffImagePlugin.EXIFIFD))

        # Delete variable
        del image, info_dictionary

        # Store the embedded XMP metadata
        if config.ENABLEXMP:
            import libxmp
            xmpfile = libxmp.XMPFiles(file_path=fullpath)
            assorted.append(str(xmpfile.get_xmp()))
            xmpfile.close_file()
        else:
            assorted.append(None)

        # Make sure we stored exactly the same amount of columns as
        # specified!!
        assert len(assorted) == len(columns)

        # Fix date format
        index = columns.index('datetime')
        if assorted[index] is not None:
            assorted[index] = dateutil.parser.parse(
                assorted[index]).isoformat()

        # Print some data that is stored in the database if debug is true
        if config.DEBUG:
            print "\nTiff file data:"
            for i in range(0, len(assorted)):
                print "%-18s %s" % (columns[i], assorted[i])

        return assorted

    except:
        traceback.print_exc(file=sys.stderr)

        # Store values in database so not the whole application crashes
        return None
Exemplo n.º 22
0
def process(file, config, rcontext, columns=None):
    fullpath = file.fullpath
    try:
        # Try to parse JPG data
        image = Image.open(fullpath, "r")

        # Create an empty list
        assorted = [
            image.app, image.applist, image.bits, image.quantization,
            image.icclist, image.huffman_dc, image.huffman_ac, image.layer,
            image.tile
        ]
        info_dictionary = image.info

        # Check if jfif is in info dictionary, if so put it in our list
        if "jfif" in info_dictionary:
            assorted.append(info_dictionary["jfif"])
            info_dictionary.pop("jfif")
        else:
            assorted.append(None)

        # Check if icc_profile is in info dictionary,
        # if so put it in our list
        if "icc_profile" in info_dictionary:
            assorted.append(info_dictionary["icc_profile"])
            info_dictionary.pop("icc_profile")
        else:
            assorted.append(None)

        # Check if jfif_version is in info dictionary,
        # if so put it in our list
        if "jfif_version" in image.info:
            assorted.append(info_dictionary["jfif_version"])
            info_dictionary.pop("jfif_version")
        else:
            assorted.append(None)

        # Check if jfif_density is in info dictionary,
        # if so put it in our list
        if "jfif_density" in image.info:
            assorted.append(info_dictionary["jfif_density"])
            info_dictionary.pop("jfif_density")
        else:
            assorted.append(None)

        # Check if jfif_unit is in info dictionary,
        # if so put it in our list
        if "jfif_unit" in image.info:
            assorted.append(info_dictionary["jfif_unit"])
            info_dictionary.pop("jfif_unit")
        else:
            assorted.append(None)

        # Check if adobe is in info dictionary,
        # if so put it in our list
        if "adobe" in info_dictionary:
            assorted.append(info_dictionary["adobe"])
            info_dictionary.pop("adobe")
        else:
            assorted.append(None)

        # Check if adobe_transform is in info dictionary,
        # if so put it in our list
        if "adobe_transform" in image.info:
            assorted.append(info_dictionary["adobe_transform"])
            info_dictionary.pop("adobe_transform")
        else:
            assorted.append(None)

        # Check if progression is in info dictionary,
        # if so put it in our list
        if "progression" in info_dictionary:
            assorted.append(info_dictionary["progression"])
            info_dictionary.pop("progression")
        else:
            assorted.append(None)

        # Check if gamma is in info dictionary,
        # if so put it in our list
        if "quality" in info_dictionary:
            assorted.append(info_dictionary["quality"])
            info_dictionary.pop("quality")
        else:
            assorted.append(None)

        # Check if optimize is in info dictionary,
        # if so put it in our list
        if "optimize" in info_dictionary:
            assorted.append(info_dictionary["optimize"][0])
            info_dictionary.pop("optimize")
        else:
            assorted.append(None)

        # Check if optimize is in info dictionary,
        # if so put it in our list
        if "progressive" in info_dictionary:
            assorted.append(info_dictionary["progressive"])
            info_dictionary.pop("progressive")
        else:
            assorted.append(None)

        # Check if flashpix is in info dictionary, if so put it in our list
        if "flashpix" in image.info:
            assorted.append(info_dictionary["flashpix"])
            info_dictionary.pop("flashpix")
        else:
            assorted.append(None)

        # Check if dpi is in info dictionary, if so put it in our list
        if "dpi" in info_dictionary:
            assorted.append(info_dictionary["dpi"][0])
            assorted.append(info_dictionary["dpi"][1])
            info_dictionary.pop("dpi")
        else:
            assorted.append(None)
            assorted.append(None)

        # Check if exif is in info dictionary,
        # if so decode and put in our list
        if "exif" in info_dictionary:
            gps_key_exists = False
            # Get all keys and check if they are in the JPEG
            for key in TAGS:
                if key == 0x8825:
                    # In Key 0x8825 some GPS data is stored
                    gps_key_exists = True
                    for gpskey in GPSTAGS:
                        assorted.append(image._getexif().get(gpskey))
                else:
                    assorted.append(image._getexif().get(key))
            if gps_key_exists == False:
                for gpskey in GPSTAGS:
                    assorted.append(None)
            info_dictionary.pop("exif")
        else:
            for key in TAGS:
                if key != 0x8825:
                    assorted.append(None)
            for gpskey in GPSTAGS:
                assorted.append(None)

        # If there are still other values in the dict
        # then put those in column
        assorted.append(info_dictionary)

        # Delete variable
        del info_dictionary, image

        # Store the embedded XMP metadata
        if config.ENABLEXMP:
            import libxmp
            xmpfile = libxmp.XMPFiles(file_path=fullpath)
            assorted.append(str(xmpfile.get_xmp()))
            xmpfile.close_file()
        else:
            assorted.append(None)

        # Make sure we stored exactly the same amount of columns as
        # specified!!
        assert len(assorted) == len(columns)

        # Print some data that is stored in the database if debug is true
        if config.DEBUG:
            print "\nJPEG file data:"
            for i in range(0, len(assorted)):
                print "%-18s %s" % (columns[i], assorted[i])
            print

        return assorted

    except:
        traceback.print_exc(file=sys.stderr)

        # Store values in database so not the whole application crashes
        return None