<commit_before>#!/usr/bin/env python
# encoding: utf-8
"""
A Python interface to the The Echo Nest's web API.  See
http://developer.echonest.com/ for details.
"""

from pyechonest.decorators import memoized
from pyechonest import util, config
import os

class Track(object):
    def __init__(self, identifier):
        if len(identifier)==18:
            self._identifier = 'music://id.echonest.com/~/TR/' + identifier
            self._md5 = None
        elif len(identifier)==32:
            self._md5 = identifier
            self._identifier = None
        elif identifier.startswith('music://'):
            self._identifier = identifier
            self._md5 = None            
        else:
            # But what if the url is 32 characters long? or 18?
            self._identifier = _upload(identifier)
            self._md5 = None
        self._name = None
        self.params = {'md5': self.md5}

    # These guys are all list of events (beats, etc)
    
    @property
    @memoized
    def bars(self):
        return parseToListOfEvents(util.call('get_bars', self.params).findall("analysis/bar"))

    @property
    @memoized
    def beats(self):
        return parseToListOfEvents(util.call('get_beats', self.params).findall("analysis/beat"))

    @property
    @memoized
    def tatums(self):
        return parseToListOfEvents(util.call('get_tatums', self.params).findall("analysis/tatum"))


    # These guys are all single float #s    
    
    @property
    @memoized
    def duration(self):
        return parseToFloat(util.call('get_duration', self.params).findall("analysis/duration"))
        
    @property
    @memoized
    def end_of_fade_in(self):
        return parseToFloat(util.call('get_end_of_fade_in', self.params).findall("analysis/end_of_fade_in"))
        
    @property
    @memoized
    def key(self):
        res = parseToFloat(util.call('get_key', self.params).findall("analysis/key"),with_confidence=True)
        res['value'] = int(res['value'])
        return res

    @property
    @memoized
    def loudness(self):
        return parseToFloat(util.call('get_loudness', self.params).findall("analysis/loudness"))

    @property
    @memoized
    def mode(self):
        res = parseToFloat(util.call('get_mode', self.params).findall("analysis/mode"),with_confidence=True)
        res['value'] = int(res['value'])
        return res

    @property
    @memoized
    def start_of_fade_out(self):
        return parseToFloat(util.call('get_start_of_fade_out', self.params).findall("analysis/start_of_fade_out"))

    @property
    @memoized
    def tempo(self):
        return parseToFloat(util.call('get_tempo', self.params).findall("analysis/tempo"),with_confidence=True)

    @property
    @memoized
    def time_signature(self):
        res = parseToFloat(util.call('get_time_signature', self.params).findall("analysis/time_signature"),with_confidence=True)
        res['value'] = int(res['value'])
        return res


    # And now the "special ones"
    
    @property
    @memoized
    def sections(self):
        tree = util.call('get_sections', self.params)
        output = []
        nodes = tree.findall('analysis/section')
        for n in nodes:
            start = float(n.attrib.get('start'))
            duration = float(n.attrib.get('duration'))
            output.append({"start":start,"duration":duration})
        return output

    @property
    @memoized
    def metadata(self):
        if self._identifier is not None:
            params = {'id':self._identifier}
        else:
            params = {'md5':self._md5}
        tree = util.call('get_metadata', params)
        output = {}
        for n in tree.findall("analysis")[0].getchildren():
            output[n.tag] = n.text
        if output.has_key('status') and output['status']=='UNKNOWN':
            raise util.EchoNestAPIThingIDError(1, "Unknown track. Please upload.")
        return output

    @property
    @memoized
    def segments(self):
        tree = util.call('get_segments', self.params)
        output = []
        nodes = tree.findall('analysis/segment')
        for n in nodes:
            start = float(n.attrib.get('start'))
            duration = float(n.attrib.get('duration'))

            loudnessnodes = n.findall('loudness/dB')
            loudness_end = None
            for l in loudnessnodes:
                if 'type' in l.attrib:
                    time_loudness_max = float(l.attrib.get('time'))
                    loudness_max = float(l.text)
                else:
                    if float(l.attrib.get('time'))!=0:
                        loudness_end = float(l.text)
                    else:
                        loudness_begin = float(l.text)

            pitchnodes = n.findall('pitches/pitch')
            pitches=[]
            for p in pitchnodes:
                pitches.append(float(p.text))

            timbrenodes = n.findall('timbre/coeff')
            timbre=[]
            for t in timbrenodes:
                timbre.append(float(t.text))

            output.append({"start":start,"duration":duration,"pitches":pitches,"timbre":timbre,"loudness_begin":loudness_begin,
                            "loudness_max":loudness_max,"time_loudness_max":time_loudness_max,"loudness_end":loudness_end})
        return output
        
    @property
    def identifier(self):
        """A unique identifier for a track.
        See http://developer.echonest.com/docs/datatypes/
        for more information"""
        if self._identifier is None:
            self._identifier = self.metadata['id']
        return self._identifier

    @property
    def name(self):
        if self._name is None:
            self._name = self.metadata['title']
        return self._name

    @property
    def md5(self):
        if self._md5 is None:
            self._md5 = self.metadata['md5']
        return self._md5

    def __repr__(self):
        return "<Track '%s'>" % self.name
    <commit_msg>if no track title, fix crash<commit_after>#!/usr/bin/env python
# encoding: utf-8
"""
A Python interface to the The Echo Nest's web API.  See
http://developer.echonest.com/ for details.
"""

from pyechonest.decorators import memoized
from pyechonest import util, config
import os

class Track(object):
    def __init__(self, identifier):
        if len(identifier)==18:
            self._identifier = 'music://id.echonest.com/~/TR/' + identifier
            self._md5 = None
        elif len(identifier)==32:
            self._md5 = identifier
            self._identifier = None
        elif identifier.startswith('music://'):
            self._identifier = identifier
            self._md5 = None            
        else:
            # But what if the url is 32 characters long? or 18?
            self._identifier = _upload(identifier)
            self._md5 = None
        self._name = None
        self.params = {'md5': self.md5}

    # These guys are all list of events (beats, etc)
    
    @property
    @memoized
    def bars(self):
        return parseToListOfEvents(util.call('get_bars', self.params).findall("analysis/bar"))

    @property
    @memoized
    def beats(self):
        return parseToListOfEvents(util.call('get_beats', self.params).findall("analysis/beat"))

    @property
    @memoized
    def tatums(self):
        return parseToListOfEvents(util.call('get_tatums', self.params).findall("analysis/tatum"))


    # These guys are all single float #s    
    
    @property
    @memoized
    def duration(self):
        return parseToFloat(util.call('get_duration', self.params).findall("analysis/duration"))
        
    @property
    @memoized
    def end_of_fade_in(self):
        return parseToFloat(util.call('get_end_of_fade_in', self.params).findall("analysis/end_of_fade_in"))
        
    @property
    @memoized
    def key(self):
        res = parseToFloat(util.call('get_key', self.params).findall("analysis/key"),with_confidence=True)
        res['value'] = int(res['value'])
        return res

    @property
    @memoized
    def loudness(self):
        return parseToFloat(util.call('get_loudness', self.params).findall("analysis/loudness"))

    @property
    @memoized
    def mode(self):
        res = parseToFloat(util.call('get_mode', self.params).findall("analysis/mode"),with_confidence=True)
        res['value'] = int(res['value'])
        return res

    @property
    @memoized
    def start_of_fade_out(self):
        return parseToFloat(util.call('get_start_of_fade_out', self.params).findall("analysis/start_of_fade_out"))

    @property
    @memoized
    def tempo(self):
        return parseToFloat(util.call('get_tempo', self.params).findall("analysis/tempo"),with_confidence=True)

    @property
    @memoized
    def time_signature(self):
        res = parseToFloat(util.call('get_time_signature', self.params).findall("analysis/time_signature"),with_confidence=True)
        res['value'] = int(res['value'])
        return res


    # And now the "special ones"
    
    @property
    @memoized
    def sections(self):
        tree = util.call('get_sections', self.params)
        output = []
        nodes = tree.findall('analysis/section')
        for n in nodes:
            start = float(n.attrib.get('start'))
            duration = float(n.attrib.get('duration'))
            output.append({"start":start,"duration":duration})
        return output

    @property
    @memoized
    def metadata(self):
        if self._identifier is not None:
            params = {'id':self._identifier}
        else:
            params = {'md5':self._md5}
        tree = util.call('get_metadata', params)
        output = {}
        for n in tree.findall("analysis")[0].getchildren():
            output[n.tag] = n.text
        if output.has_key('status') and output['status']=='UNKNOWN':
            raise util.EchoNestAPIThingIDError(1, "Unknown track. Please upload.")
        return output

    @property
    @memoized
    def segments(self):
        tree = util.call('get_segments', self.params)
        output = []
        nodes = tree.findall('analysis/segment')
        for n in nodes:
            start = float(n.attrib.get('start'))
            duration = float(n.attrib.get('duration'))

            loudnessnodes = n.findall('loudness/dB')
            loudness_end = None
            for l in loudnessnodes:
                if 'type' in l.attrib:
                    time_loudness_max = float(l.attrib.get('time'))
                    loudness_max = float(l.text)
                else:
                    if float(l.attrib.get('time'))!=0:
                        loudness_end = float(l.text)
                    else:
                        loudness_begin = float(l.text)

            pitchnodes = n.findall('pitches/pitch')
            pitches=[]
            for p in pitchnodes:
                pitches.append(float(p.text))

            timbrenodes = n.findall('timbre/coeff')
            timbre=[]
            for t in timbrenodes:
                timbre.append(float(t.text))

            output.append({"start":start,"duration":duration,"pitches":pitches,"timbre":timbre,"loudness_begin":loudness_begin,
                            "loudness_max":loudness_max,"time_loudness_max":time_loudness_max,"loudness_end":loudness_end})
        return output
        
    @property
    def identifier(self):
        """A unique identifier for a track.
        See http://developer.echonest.com/docs/datatypes/
        for more information"""
        if self._identifier is None:
            self._identifier = self.metadata['id']
        return self._identifier

    @property
    def name(self):
        if self._name is None:
            try:
                self._name = self.metadata['title']
            except KeyError:
                self._name = ""
        return self._name

    @property
    def md5(self):
        if self._md5 is None:
            self._md5 = self.metadata['md5']
        return self._md5

    def __repr__(self):
        return "<Track '%s'>" % self.name
    
{'fraction_non_alphanumeric': 0.12097244192823245, 'fraction_numerical': 0.004162850720173175, 'mean_word_length': 2.209190488912637, 'pattern_counts': {'":': 20, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3914522', 'n_tokens_mistral': 3642, 'n_tokens_neox': 3531, 'n_words': 715}