#! /usr/bin/python

"""

This file takes a directory or a list of plugins.tar.bz2 files as arguments,
and prints a yaml string containing the description of all plugins found in
these files.

"""
import sys, os

def parse_args():
    from optparse import OptionParser
    usage = "usage: %s [options] <path>" % sys.argv[0]
    parser = OptionParser(usage=usage)
    options, args = parser.parse_args()
    if len(args) == 0: print usage; sys.exit(2)
    else: return options, args

def list_available_bundle_archives(path):
    from glob import glob
    if os.path.isdir(path):
        return glob(os.path.join(path, '*'))
    else:
        return glob(path)

def list_bundle_archive_content(archive_file):
    import tarfile
    tar = tarfile.open(archive_file, 'r')
    files = tar.getnames()
    tar.close()
    return files

def test_is_loadable(filename): 
    import gst
    try:
      plugin = gst.plugin_load_file(filename)
      return (plugin != None)
    except:
      return False

def load_plugin(plugin_name):
    import gst
    if os.path.isfile(plugin_name):
        plugin = gst.plugin_load_file(plugin_name)
    else:
        plugin = gst.plugin_load_by_name(plugin_name)
    if plugin == None:
        print "ERROR: could not load %s" % plugin_name
    return plugin

def get_list_of_caps(plugin_name):
    """ list encoder caps and uri source from a plugin name or file path """
    import gst, os
    plugin = load_plugin(plugin_name)

    registry = gst.registry_get_default()
    caps_list = [] 
    for element in registry.get_feature_list_by_plugin(plugin.get_name()):
        element_type = type(element)
        if ( element_type != gst.ElementFactory ):
            pass 
        else:
            klasses = element.get_klass().split('/')
            if ( 'Generic' in klasses ):
                pass
            elif 'Codec' in klasses:
                for pad in element.get_static_pad_templates():
                    if pad.direction in [gst.PAD_SINK,]:
                        caps = pad.get_caps()
                        if caps == 'ANY':
                            continue
                        this_caps = ''
                        if 'Decoder' in klasses or 'Demuxer' in klasses:
                            this_caps = 'decoder-' + '; decoder-'.join( str(caps).split('; ') )  + '; '
                        # Only add the element if it has some valid decoders/demuxers/...
                        if this_caps:
                            caps_list.append ( 'element-' + element.get_name()+ '; ' + this_caps  )
            elif ( 'Source' in klasses ):
                for uri in element.get_uri_protocols():
                    caps_list.append ( 'element-' + element.get_name() + '; urisource-' + uri+ '; '  )
    #if len(caps_list) == 0: print "ERR: no caps for %s" % plugin.get_name()
    return caps_list

def get_plugin_info(plugin_file):
    info = {}
    if test_is_loadable(plugin_file):
        plugin = load_plugin(plugin_file)
        info['name'] = plugin.get_name()
        info['caps'] = get_list_of_caps(plugin_file)
        info['description'] = plugin.get_description()
        info['license'] = plugin.get_license()
        info['version'] = plugin.get_version()
        info['origin'] = plugin.get_origin()
    else:
        info['name'] = re.sub(r'^libgst(.*).so', r'\1', os.path.basename(plugin_file) )
        info['caps'] = ''
        info['description'] = ''
        info['license'] = ''
        info['version'] = ''
        info['origin'] = ''
    return info 

def scan_bundle_archive(archive_file):
    # extract all files that are not documentation
    from glob import glob
    import tarfile, tempfile
    import gst
    tdir = tempfile.mkdtemp(prefix='fluendo-bundle-')
    tar = tarfile.open(archive_file, 'r')
    files = tar.getnames()
    for file in files:
        tar.extract(file, tdir)
    tar.close()
    plugin_files = [os.path.join(tdir,file) for file in files]
    plugin_files = filter( lambda f: f.endswith('.so'), plugin_files )
    for plugin_file in plugin_files:
        yield get_plugin_info(plugin_file) 
    os.system("rm -rf '%s'" % tdir)

def scan_deb_archive(archive_file):
    # extract all files that are not documentation
    from glob import glob
    from debian_bundle import arfile
    import tarfile
    import tempfile
    import gst
    tdir = tempfile.mkdtemp(prefix='fluendo-bundle-')
    a = arfile.ArFile(archive_file)
    files = a.getnames()
    target = open(os.path.join(tdir,'data.tar.gz'), 'w')
    target.write(a['data.tar.gz'].read())
    target.close()
    tar = tarfile.open(os.path.join(tdir,'data.tar.gz'), 'r')
    files = tar.getnames()
    for file in files:
        tar.extract(file, tdir)
    tar.close()
    plugin_files = [os.path.join(tdir,file) for file in files]
    plugin_files = filter( lambda f: f.endswith('.so'), plugin_files )
    for plugin_file in plugin_files:
        yield get_plugin_info(plugin_file) 
    os.system("rm -rf '%s'" % tdir)

def scan_rpm_archive(archive_file):
    # extract all files that are not documentation
    from glob import glob
    import rpm
    import tempfile
    import gst
    tdir = tempfile.mkdtemp(prefix='fluendo-bundle-')
    os.system('cd %s && rpm2cpio %s | cpio -i --make-directories' % (tdir, os.path.realpath(archive_file)) )
    plugin_files = glob(os.path.join(tdir,'usr/*/gstreamer*/*.so'))
    for plugin_file in plugin_files:
        yield get_plugin_info(plugin_file) 
    os.system("rm -rf '%s'" % tdir)

def build_bundle_dic(args, this_arch):
    available_bundles = {}
    
    for path in args:
        for archive_file in list_available_bundle_archives(path):
            if archive_file.endswith('.tar.bz2'):
                # create bundle if it does not exist
                bundle_name, architecture = os.path.basename(archive_file).split('.')[0:2]
                if bundle_name not in available_bundles: available_bundles[bundle_name] = {}
                if 'architectures' not in available_bundles[bundle_name]:
                    available_bundles[bundle_name]['architectures'] = []
                available_bundles[bundle_name]['architectures'] += [architecture]
                available_bundles[bundle_name]['name'] = '-'.join(bundle_name.split('-')[1:-1])
                available_bundles[bundle_name]['version'] = '-'.join(bundle_name.split('-')[-1])
                available_bundles[bundle_name]['plugins'] = []
                for info in scan_bundle_archive(archive_file):
                    available_bundles[bundle_name]['plugins'] += [info]
            elif archive_file.endswith('.deb'):
                # create bundle if it does not exist
                from debian_bundle.debfile import DebFile, DebError
                try:
                    debfile = DebFile(filename=archive_file)
                except DebError:
                    continue
                control = debfile.control.debcontrol()
                bundle_name, version, architecture = control['Package'], \
                  control['Version'], control['Architecture'] 
                if bundle_name not in available_bundles: available_bundles[bundle_name] = {}
                available_bundles[bundle_name]['name'] = bundle_name 
                available_bundles[bundle_name]['version'] = version 
                if 'architectures' not in available_bundles[bundle_name]:
                    available_bundles[bundle_name]['architectures'] = []
                available_bundles[bundle_name]['architectures'] += [architecture]
                available_bundles[bundle_name]['plugins'] = []
                available_bundles[bundle_name]['description'] = control['Description'].split('\n')[0]
                for info in scan_deb_archive(archive_file):
                    available_bundles[bundle_name]['plugins'] += [info]
                #print bundle_name, version, architecture
            elif archive_file.endswith('.rpm'):
                import rpm
                ts = rpm.ts()
                fdno = os.open(archive_file, os.O_RDONLY)
                try:
                    hdr = ts.hdrFromFdno(fdno)
                except rpm.error:
                    fdno = os.open(archive_file, os.O_RDONLY)
                    ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
                    hdr = ts.hdrFromFdno(fdno)
                os.close(fdno)
                tmpname = os.path.splitext(os.path.basename(archive_file))[0]
                bundle_name = hdr[rpm.RPMTAG_NAME] 
                version = hdr[rpm.RPMTAG_VERSION] 
                architecture = hdr[rpm.RPMTAG_ARCH] 
                if bundle_name not in available_bundles: available_bundles[bundle_name] = {}
                available_bundles[bundle_name]['name'] = bundle_name 
                available_bundles[bundle_name]['version'] = version 
                if 'architectures' not in available_bundles[bundle_name]:
                    available_bundles[bundle_name]['architectures'] = []
                available_bundles[bundle_name]['architectures'] += [architecture]
                available_bundles[bundle_name]['plugins'] = []
                for info in scan_rpm_archive(archive_file):
                    available_bundles[bundle_name]['plugins'] += [info]
    return available_bundles

def yaml_string (string):
    return '"'+str(string).replace('"', "'")+'"'

if __name__ == '__main__':
    import re
    this_arch = 'i386'
    options, args = parse_args()
    available_bundles = build_bundle_dic(args, this_arch)

    print "# WARNING: this file is generated. Do *NOT* edit!"
    print "products:"
    for bundle_name in available_bundles:
        print "  -"
        # Sort the architecture to prevent cont changes in SVN
        available_bundles[bundle_name]['architectures'].sort ()
        for each in ['name', 'version', 'architectures']:
            print "    %s:" % each, str(available_bundles[bundle_name][each])
        if 'description' in available_bundles[bundle_name]: 
            print "    description:", yaml_string(available_bundles[bundle_name]['description'])
        print "    plugins:"
        for info in available_bundles[bundle_name]['plugins']:
            print "      -"
            for each in ['name', 'version', 'description', 'origin', 'caps']:
                if isinstance (info[each], list):
                    string = info[each]
                else:
                    string = yaml_string (info[each])
                print "        %s:" % each, string
        print
