Update of /cvsroot/freevo/kaa/metadata/src/disc
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14035/src/disc

Added Files:
        .cvsignore CDDB.py DiscID.py __init__.py audioinfo.py 
        cdrommodule.c datainfo.py discinfo.py dvdinfo.py ifomodule.c 
        lsdvd.py vcdinfo.py 
Log Message:
move current mmpython cvs to kaa.metadata

--- NEW FILE: vcdinfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# vcdinfo.py - parse vcd track informations
# -----------------------------------------------------------------------------
# $Id: vcdinfo.py,v 1.1 2005/07/02 16:33:11 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# kaa imports
from kaa.metadata import factory
from kaa.metadata import mediainfo
from discinfo import DiscInfo
import cdrom

class VCDInfo(DiscInfo):
    def __init__(self,device):
        DiscInfo.__init__(self)
        self.context = 'video'
        self.offset = 0
        self.mime = 'video/vcd'
        self.type = 'CD'        
        self.subtype = 'video'
        # parse disc
        self.parseDisc(device)


    def parseDisc(self, device):
        type = None
        if DiscInfo.isDisc(self, device) != 2:
            raise mediainfo.KaaMetadataParseError()
        
        # brute force reading of the device to find out if it is a VCD
        f = open(device,'rb')
        f.seek(32768, 0)
        buffer = f.read(60000)
        f.close()

        if buffer.find('SVCD') > 0 and buffer.find('TRACKS.SVD') > 0 and \
               buffer.find('ENTRIES.SVD') > 0:
            type = 'SVCD'

        elif buffer.find('INFO.VCD') > 0 and buffer.find('ENTRIES.VCD') > 0:
            type = 'VCD'

        else:
            raise mediainfo.KaaMetadataParseError()

        # read the tracks to generate the title list
        device = open(device)
        (first, last) = cdrom.toc_header(device)

        lmin = 0
        lsec = 0

        num = 0
        for i in range(first, last + 2):
            if i == last + 1:
                min, sec, frames = cdrom.leadout(device)
            else:
                min, sec, frames = cdrom.toc_entry(device, i)
            if num:
                vi = mediainfo.VideoInfo()
                # XXX add more static information here, it's also possible
                # XXX to scan for more informations like fps
                # XXX Settings to MPEG1/2 is a wild guess, maybe the track
                # XXX isn't playable at all (e.g. the menu)
                if type == 'VCD':
                    vi.codec = 'MPEG1'
                else:
                    vi.codec = 'MPEG2'
                vi.length = (min-lmin) * 60 + (sec-lsec)
                self.tracks.append(vi)
            num += 1
            lmin, lsec = min, sec
        device.close()

    
factory.register( 'video/vcd', mediainfo.EXTENSION_DEVICE,
                       mediainfo.TYPE_AV, VCDInfo )

--- NEW FILE: .cvsignore ---
*.pyc *.pyo

--- NEW FILE: dvdinfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# dvdinfo.py - parse dvd title structure
# -----------------------------------------------------------------------------
# $Id: dvdinfo.py,v 1.1 2005/07/02 16:33:11 dischi Exp $
#
# TODO: update the ifomodule and remove the lsdvd parser
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# python imports
import os
import logging

# kaa imports
import ifoparser
from kaa.metadata import mediainfo
from kaa.metadata import factory
from discinfo import DiscInfo

# get logging object
log = logging.getLogger('metadata')

class DVDAudio(mediainfo.AudioInfo):
    def __init__(self, title, number):
        mediainfo.AudioInfo.__init__(self)
        self.number = number
        self.title  = title
        self.id, self.language, self.codec, self.channels, self.samplerate = \
                 ifoparser.audio(title, number)


class DVDTitle(mediainfo.AVInfo):
    def __init__(self, number):
        mediainfo.AVInfo.__init__(self)
        self.number = number
        self.chapters, self.angles, self.length, audio_num, \
                       subtitles_num = ifoparser.title(number)

        self.keys.append('chapters')
        self.keys.append('subtitles')

        self.mime = 'video/mpeg'
        for a in range(1, audio_num+1):
            self.audio.append(DVDAudio(number, a))

        for s in range(1, subtitles_num+1):
            self.subtitles.append(ifoparser.subtitle(number, s)[0])


class DVDInfo(DiscInfo):
    def __init__(self, device):
        DiscInfo.__init__(self)
        self.context = 'video'
        self.offset = 0

        log.info('trying buggy dvd detection')

        if isinstance(device, file):
            self.parseDVDiso(device)
        elif os.path.isdir(device):
            self.parseDVDdir(device)
        else:
            self.parseDisc(device)

        self.keys.append('length')
        self.length = 0
        first       = 0

        for t in self.tracks:
            self.length += t.length
            if not first:
                first = t.length

        if self.length/len(self.tracks) == first:
            # badly mastered dvd
            self.length = first

        self.mime    = 'video/dvd'
        self.type    = 'DVD'
        self.subtype = 'video'


    def parseDVDdir(self, dirname):
        if not (os.path.isdir(dirname+'/VIDEO_TS') or \
                os.path.isdir(dirname+'/video_ts') or \
                os.path.isdir(dirname+'/Video_ts')):
            raise mediainfo.KaaMetadataParseError()

        # OK, try libdvdread
        title_num = ifoparser.open(dirname)
        if not title_num:
            raise mediainfo.KaaMetadataParseError()

        for title in range(1, title_num+1):
            ti = DVDTitle(title)
            ti.trackno = title
            ti.trackof = title_num
            self.appendtrack(ti)

        ifoparser.close()
        return 1


    def parseDisc(self, device):
        if DiscInfo.isDisc(self, device) != 2:
            raise mediainfo.KaaMetadataParseError()

        # brute force reading of the device to find out if it is a DVD
        f = open(device,'rb')
        f.seek(32768, 0)
        buffer = f.read(60000)

        if buffer.find('UDF') == -1:
            f.close()
            raise mediainfo.KaaMetadataParseError()

        # seems to be a DVD, read a little bit more
        buffer += f.read(550000)
        f.close()

        if buffer.find('VIDEO_TS') == -1 and \
               buffer.find('VIDEO_TS.IFO') == -1 and \
               buffer.find('OSTA UDF Compliant') == -1:
            raise mediainfo.KaaMetadataParseError()

        # OK, try libdvdread
        title_num = ifoparser.open(device)

        if not title_num:
            raise mediainfo.KaaMetadataParseError()

        for title in range(1, title_num+1):
            ti = DVDTitle(title)
            ti.trackno = title
            ti.trackof = title_num
            self.appendtrack(ti)

        ifoparser.close()


    def parseDVDiso(self, f):
        # brute force reading of the device to find out if it is a DVD
        f.seek(32768, 0)
        buffer = f.read(60000)

        if buffer.find('UDF') == -1:
            raise mediainfo.KaaMetadataParseError()

        # seems to be a DVD, read a little bit more
        buffer += f.read(550000)

        if buffer.find('VIDEO_TS') == -1 and \
               buffer.find('VIDEO_TS.IFO') == -1 and \
               buffer.find('OSTA UDF Compliant') == -1:
            raise mediainfo.KaaMetadataParseError()

        # OK, try libdvdread
        title_num = ifoparser.open(f.name)

        if not title_num:
            raise mediainfo.KaaMetadataParseError()

        for title in range(1, title_num+1):
            ti = DVDTitle(title)
            ti.trackno = title
            ti.trackof = title_num
            self.appendtrack(ti)

        ifoparser.close()


if not factory.gettype('video/dvd', mediainfo.EXTENSION_DEVICE):
    factory.register( 'video/dvd', mediainfo.EXTENSION_DEVICE,
                      mediainfo.TYPE_AV, DVDInfo )

if not factory.gettype('video/dvd', mediainfo.EXTENSION_DIRECTORY):
    factory.register('video/dvd', mediainfo.EXTENSION_DIRECTORY,
                     mediainfo.TYPE_AV, DVDInfo)

factory.register('video/dvd', ['iso'], mediainfo.TYPE_AV, DVDInfo)

--- NEW FILE: cdrommodule.c ---
/* 
 * cdrommodule.c 
 * Python extension module for reading in audio CD-ROM data
 *
 * Please port me to other OSes besides Linux, Solaris, OpenBSD,
 * and FreeBSD! 
 *
 * See the README for info.
 *
 * Written 17 Nov 1999 by Ben Gertzfield <[EMAIL PROTECTED]>
 * This work is released under the GNU GPL, version 2 or later.
 *
 * FreeBSD support by Michael Yoon <[EMAIL PROTECTED]>
 * OpenBSD support added by Alexander Guy <[EMAIL PROTECTED]>
 *
 * Thanks to Viktor Fougstedt <[EMAIL PROTECTED]> for info
 * on the <sys/cdio.h> include file to make this work on Solaris!
 *
 * Release version 1.2
 *
 *
 *
 * changes for kaa.metadata:
 *
 * Fixed bug in the cdrommodule that the file was not closed after usage.
 * The result was a drive you can't eject while the program (e.g. Freevo)
 * is running. 
 *
 *
 * CVS ID: $Id: cdrommodule.c,v 1.1 2005/07/02 16:33:11 dischi Exp $
 */

#include "Python.h"
#include <fcntl.h>
#include <sys/ioctl.h>

#ifdef __linux__
#include <linux/cdrom.h>
#endif

#if defined(sun) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/cdio.h>
#endif

/* 
 * Since FreeBSD has identical support but different names for lots
 * of these structs and constants, we'll just #define CDDB_WHATEVER
 * so that we don't have to repeat the code.
 */

#ifdef __FreeBSD__

#define CDDB_TOC_HEADER_STRUCT ioc_toc_header 
#define CDDB_STARTING_TRACK_FIELD starting_track 
#define CDDB_ENDING_TRACK_FIELD ending_track
#define CDDB_READ_TOC_HEADER_FLAG CDIOREADTOCHEADER 
#define CDDB_TOC_ENTRY_STRUCT ioc_read_toc_single_entry 
#define CDDB_TRACK_FIELD track 
#define CDDB_FORMAT_FIELD address_format 
#define CDDB_MSF_FORMAT CD_MSF_FORMAT 
#define CDDB_ADDR_FIELD entry.addr 
#define CDDB_READ_TOC_ENTRY_FLAG CDIOREADTOCENTRY 
#define CDDB_CDROM_LEADOUT 0xaa 
#define CDDB_DEFAULT_CDROM_DEVICE "/dev/cdrom"
#define CDDB_DEFAULT_CDROM_FLAGS 0

#elif defined(__OpenBSD__)

#define CDDB_TOC_HEADER_STRUCT ioc_toc_header 
#define CDDB_STARTING_TRACK_FIELD starting_track 
#define CDDB_ENDING_TRACK_FIELD ending_track
#define CDDB_READ_TOC_HEADER_FLAG CDIOREADTOCHEADER 
#define CDDB_TOC_ENTRY_STRUCT ioc_read_toc_entry 
#define CDDB_TRACK_FIELD starting_track 
#define CDDB_FORMAT_FIELD address_format 
#define CDDB_MSF_FORMAT CD_MSF_FORMAT  
#define CDDB_ADDR_FIELD data->addr 
#define CDDB_READ_TOC_ENTRY_FLAG CDIOREADTOCENTRIES
#define CDDB_CDROM_LEADOUT 0xaa 
#define CDDB_DEFAULT_CDROM_DEVICE "/dev/cdrom"
#define CDDB_DEFAULT_CDROM_FLAGS 0

#else /* Linux and Solaris */

#define CDDB_TOC_HEADER_STRUCT cdrom_tochdr 
#define CDDB_STARTING_TRACK_FIELD cdth_trk0 
#define CDDB_ENDING_TRACK_FIELD cdth_trk1 
#define CDDB_READ_TOC_HEADER_FLAG CDROMREADTOCHDR 
#define CDDB_TOC_ENTRY_STRUCT cdrom_tocentry
#define CDDB_TRACK_FIELD cdte_track 
#define CDDB_FORMAT_FIELD cdte_format 
#define CDDB_MSF_FORMAT CDROM_MSF 
#define CDDB_ADDR_FIELD cdte_addr 
#define CDDB_READ_TOC_ENTRY_FLAG CDROMREADTOCENTRY 
#define CDDB_CDROM_LEADOUT CDROM_LEADOUT

#ifdef sun
#define CDDB_DEFAULT_CDROM_DEVICE "/dev/vol/alias/cdrom0"
#else
#define CDDB_DEFAULT_CDROM_DEVICE "/dev/cdrom"
#endif /* sun */

#define CDDB_DEFAULT_CDROM_FLAGS O_RDONLY | O_NONBLOCK

#endif /* __FreeBSD__ */

static PyObject *cdrom_error;

static PyObject *cdrom_toc_header(PyObject *self, PyObject *args)
{
    struct CDDB_TOC_HEADER_STRUCT hdr;
    PyObject *cdrom_fileobj;
    int cdrom_fd;

    if (!PyArg_ParseTuple(args, "O!", &PyFile_Type, &cdrom_fileobj))
        return NULL;

    cdrom_fd = fileno(PyFile_AsFile(cdrom_fileobj));

    if (ioctl(cdrom_fd, CDDB_READ_TOC_HEADER_FLAG, &hdr) < 0) {
        PyErr_SetFromErrno(cdrom_error);
        return NULL;
    }
    
    return Py_BuildValue("bb", hdr.CDDB_STARTING_TRACK_FIELD, 
                         hdr.CDDB_ENDING_TRACK_FIELD);
}

static PyObject *cdrom_toc_entry(PyObject *self, PyObject *args)
{
    struct CDDB_TOC_ENTRY_STRUCT entry;
    PyObject *cdrom_fileobj;
    int cdrom_fd;
    unsigned char track;

#if defined(__OpenBSD__)
    struct cd_toc_entry data;
#endif

    if (!PyArg_ParseTuple(args, "O!b", &PyFile_Type, &cdrom_fileobj, &track))
        return  NULL;

    cdrom_fd = fileno(PyFile_AsFile(cdrom_fileobj));

    entry.CDDB_TRACK_FIELD = track;
    entry.CDDB_FORMAT_FIELD = CDDB_MSF_FORMAT;

#if defined(__OpenBSD__)
    entry.data = &data;
    entry.data_len = sizeof(data);
#endif

    if (ioctl(cdrom_fd, CDDB_READ_TOC_ENTRY_FLAG, &entry) < 0) {
        PyErr_SetFromErrno(cdrom_error);
        return NULL;
    }

    return Py_BuildValue("bbb", entry.CDDB_ADDR_FIELD.msf.minute, 
                         entry.CDDB_ADDR_FIELD.msf.second, 
                         entry.CDDB_ADDR_FIELD.msf.frame);
}

static PyObject *cdrom_leadout(PyObject *self, PyObject *args)
{
    struct CDDB_TOC_ENTRY_STRUCT entry;
    PyObject *cdrom_fileobj;
    int cdrom_fd;

#if defined(__OpenBSD__)
    struct cd_toc_entry data;
#endif

    if (!PyArg_ParseTuple(args, "O!", &PyFile_Type, &cdrom_fileobj))
        return  NULL;

    cdrom_fd = fileno(PyFile_AsFile(cdrom_fileobj));

    entry.CDDB_TRACK_FIELD = CDDB_CDROM_LEADOUT;
    entry.CDDB_FORMAT_FIELD = CDDB_MSF_FORMAT;

#if defined(__OpenBSD__)
    entry.data = &data;
    entry.data_len = sizeof(data);
#endif

    if (ioctl(cdrom_fd, CDDB_READ_TOC_ENTRY_FLAG, &entry) < 0) {
        PyErr_SetFromErrno(cdrom_error);
        return NULL;
    }

    return Py_BuildValue("bbb", entry.CDDB_ADDR_FIELD.msf.minute, 
                         entry.CDDB_ADDR_FIELD.msf.second, 
                         entry.CDDB_ADDR_FIELD.msf.frame);
}

int cdrom_close(FILE *cdrom_file) 
{
  return 0;
}

static PyObject *cdrom_really_close(PyObject *self, PyObject *args)
{
    PyObject *cdrom_fileobj;
    int cdrom_fd;

#if defined(__OpenBSD__)
    struct cd_toc_entry data;
#endif

    if (!PyArg_ParseTuple(args, "O!", &PyFile_Type, &cdrom_fileobj))
        return  NULL;

    cdrom_fd = fileno(PyFile_AsFile(cdrom_fileobj));
    close(cdrom_fd);
    return Py_BuildValue("b", 1);
}

static PyObject* cdrom_open(PyObject *self, PyObject *args)
{
    int cdrom_fd;
    FILE *cdrom_file;
    char *cdrom_device = CDDB_DEFAULT_CDROM_DEVICE;
    int cdrom_open_flags = CDDB_DEFAULT_CDROM_FLAGS;

    PyObject *cdrom_file_object;

    if (!PyArg_ParseTuple(args, "|si", &cdrom_device, &cdrom_open_flags))
        return NULL;

    cdrom_fd = open(cdrom_device, cdrom_open_flags);

    if (cdrom_fd == -1) {
        PyErr_SetFromErrno(cdrom_error);
        return NULL;
    }
    
    cdrom_file = fdopen(cdrom_fd, "r");

    if (cdrom_file == NULL) {
        PyErr_SetFromErrno(cdrom_error);
        return NULL;
    }

    cdrom_file_object = PyFile_FromFile(cdrom_file, cdrom_device, "r", 
cdrom_close);

    if (cdrom_file_object == NULL) {
        PyErr_SetString(cdrom_error, "Internal conversion from file pointer to 
Python object failed");
        fclose(cdrom_file);
        return NULL;
    }

    return Py_BuildValue("O", cdrom_file_object);
}

static PyMethodDef cdrom_methods[] = {
    { "toc_header", cdrom_toc_header, METH_VARARGS },
    { "toc_entry", cdrom_toc_entry, METH_VARARGS },
    { "leadout", cdrom_leadout, METH_VARARGS},
    { "open", cdrom_open, METH_VARARGS},
    { "close", cdrom_really_close, METH_VARARGS},
    { NULL, NULL }
};

void initcdrom(void)
{
    PyObject *module, *dict;

    module = Py_InitModule("cdrom", cdrom_methods);
    dict = PyModule_GetDict(module);
    cdrom_error = PyErr_NewException("cdrom.error", NULL, NULL);
    PyDict_SetItemString(dict, "error", cdrom_error);
}

--- NEW FILE: __init__.py ---

--- NEW FILE: discinfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# discinfo.py - basic class for any discs containing collections of media.
# -----------------------------------------------------------------------------
# $Id: discinfo.py,v 1.1 2005/07/02 16:33:11 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# freevo imports
import os
import re
import time
import array
import md5
from struct import *
import logging

# kaa imports
from kaa.metadata import mediainfo

# get logging object
log = logging.getLogger('metadata')

CREATE_MD5_ID = 0

try:
    from fcntl import ioctl
    import DiscID
except:
    log.warning('WARNING: failed to import ioctl, discinfo won\'t work')


def cdrom_disc_status(device, handle_mix = 0):
    """
    check the current disc in device
    return: no disc (0), audio cd (1), data cd (2), blank cd (3)
    """
    CDROM_DRIVE_STATUS=0x5326
    CDSL_CURRENT=( (int ) ( ~ 0 >> 1 ) )
    CDROM_DISC_STATUS=0x5327
    CDS_AUDIO=100
    CDS_MIXED=105
    CDS_DISC_OK=4

    # FreeBSD ioctls - there is no CDROM.py
    # XXX 0xc0086305 below creates a warning, but 0xc0086305L
    # doesn't work. Suppress that warning for Linux users,
    # until a better solution can be found.
    if os.uname()[0] == 'FreeBSD':
        CDIOREADTOCENTRYS = 0xc0086305L
        CD_MSF_FORMAT = 2

    try:
        fd = os.open(device, os.O_RDONLY | os.O_NONBLOCK)
        if os.uname()[0] == 'FreeBSD':
            try:
                cd_toc_entry = array.array('c', '\000'*4096)
                (address, length) = cd_toc_entry.buffer_info()
                buf = pack('BBHP', CD_MSF_FORMAT, 0, length, address)
                s = ioctl(fd, CDIOREADTOCENTRYS, buf)
                s = CDS_DISC_OK
            except:
                s = CDS_NO_DISC
        else:
            s = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)
    except:
        log.error('ERROR: no permission to read %s' % device)
        log.error('Media detection not possible, set drive to \'empty\'')

        # maybe we need to close the fd if ioctl fails, maybe
        # open fails and there is no fd, maye we aren't running
        # linux and don't have ioctl
        try:
            os.close(fd)
        except:
            pass
        return 0

    if not s == CDS_DISC_OK:
        # no disc, error, whatever
        try:
            os.close(fd)
        except:
            pass
        return 0

    if os.uname()[0] == 'FreeBSD':
        s = 0
        # We already have the TOC from above
        for i in range(0, 4096, 8):
            control = unpack('B', cd_toc_entry[i+1])[0] & 4
            track = unpack('B', cd_toc_entry[i+2])[0]
            if track == 0:
                break
            if control == 0 and s != CDS_MIXED:
                s = CDS_AUDIO
            elif control != 0:
                if s == CDS_AUDIO:
                    s = CDS_MIXED
                else:
                    s = 100 + control # ugly, but encodes Linux ioctl returns
            elif control == 5:
                s = CDS_MIXED

    else:
        s = ioctl(fd, CDROM_DISC_STATUS)
    os.close(fd)

    if s == CDS_MIXED and handle_mix:
        return 4
    if s == CDS_AUDIO or s == CDS_MIXED:
        return 1

    try:
        fd = open(device, 'rb')
        # try to read from the disc to get information if the disc
        # is a rw medium not written yet

        fd.seek(32768) # 2048 multiple boundary for FreeBSD
        # FreeBSD doesn't return IOError unless we try and read:
        fd.read(1)
    except IOError:
        # not readable
        fd.close()
        return 3

    # disc ok
    fd.close()
    return 2


id_cache = {}

def cdrom_disc_id(device, handle_mix=0):
    """
    return the disc id of the device or None if no disc is there
    """
    global id_cache
    try:
        if id_cache[device][0] + 0.9 > time.time():
            return id_cache[device][1:]
    except:
        pass

    disc_type = cdrom_disc_status(device, handle_mix=handle_mix)
    if disc_type == 0 or disc_type == 3:
        return 0, None

    elif disc_type == 1 or disc_type == 4:
        disc_id = DiscID.disc_id(device)
        id = '%08lx_%d' % (disc_id[0], disc_id[1])
    else:
        f = open(device,'rb')

        if os.uname()[0] == 'FreeBSD':
            # FreeBSD can only seek to 2048 multiple boundaries.
            # Below works on Linux and FreeBSD:
            f.seek(32768)
            id = f.read(829)
            label = id[40:72]
            id = id[813:829]
        else:
            f.seek(0x0000832d)
            id = f.read(16)
            f.seek(32808, 0)
            label = f.read(32)

        if CREATE_MD5_ID:
            id_md5 = md5.new()
            id_md5.update(f.read(51200))
            id = id_md5.hexdigest()

        f.close()

        m = re.match("^(.*[^ ]) *$", label)
        if m:
            id = '%s%s' % (id, m.group(1))
        id = re.compile('[^a-zA-Z0-9()_-]').sub('_', id)


    id_cache[device] = time.time(), disc_type, id
    id = id.replace('/','_')
    return disc_type, id


class DiscInfo(mediainfo.CollectionInfo):
    def isDisc(self, device):
        (type, self.id) = cdrom_disc_id(device, handle_mix=1)
        if type != 2:
            if type == 4:
                self.keys.append('mixed')
                self.mixed = 1
                type = 1
            return type

        if CREATE_MD5_ID:
            if len(self.id) == 32:
                self.label = ''
            else:
                self.label = self.id[32:]
        else:
            if len(self.id) == 16:
                self.label = ''
            else:
                self.label = self.id[16:]

        self.keys.append('label')
        return type

--- NEW FILE: ifomodule.c ---
//based on http://arnfast.net/projects/ifoinfo.php by Jens Arnfast 

#include <Python.h>

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>

#include <dvdread/dvd_reader.h>
#include <dvdread/ifo_types.h>
#include <dvdread/ifo_read.h>

static PyObject *ifoinfo_open(PyObject *self, PyObject *args);
static PyObject *ifoinfo_close(PyObject *self, PyObject *args);

static PyObject *ifoinfo_read_title(PyObject *self, PyObject *args);
static PyObject *ifoinfo_get_audio_tracks(PyObject *self, PyObject *args);
static PyObject *ifoinfo_get_subtitle_tracks(PyObject *self, PyObject *args);

static PyMethodDef IfoMethods[] = {
  {"open",  ifoinfo_open, METH_VARARGS},
  {"close",  ifoinfo_close, METH_VARARGS},
  {"title", ifoinfo_read_title, METH_VARARGS},
  {"audio", ifoinfo_get_audio_tracks, METH_VARARGS},
  {"subtitle", ifoinfo_get_subtitle_tracks, METH_VARARGS},
  {NULL, NULL}
};


void initifoparser() {
   (void) Py_InitModule("ifoparser", IfoMethods);
}


dvd_reader_t *dvd;
ifo_handle_t *ifofile;

static PyObject *ifoinfo_open(PyObject *self, PyObject *args) {
  tt_srpt_t *tt_srpt;
  int i, ch, gotopt = -1, dochapters = -1;
  char *dvddevice;

  if (!PyArg_ParseTuple(args, "s", &dvddevice))
    return Py_BuildValue("i", 0);

  dvd = DVDOpen(dvddevice);
  
  if (!dvd)
    return Py_BuildValue("i", 0);
  
  ifofile = ifoOpen(dvd, 0);
  if (!ifofile) {
    DVDClose(dvd);
    return Py_BuildValue("i", 0);
  }

  tt_srpt = ifofile->tt_srpt;
  return Py_BuildValue("i", tt_srpt->nr_of_srpts);
}


static PyObject *ifoinfo_close(PyObject *self, PyObject *args) {
  ifoClose(ifofile);
  DVDClose(dvd);
  return Py_BuildValue("i", 0);
}


static PyObject *ifoinfo_read_title(PyObject *self, PyObject *args) {
  int i;

  tt_srpt_t *tt_srpt;
  ifo_handle_t *vtsfile;
  int vtsnum, ttnnum, j;
  long playtime;

  if (!PyArg_ParseTuple(args, "i", &i))
    return Py_BuildValue("(iiiii)", 0, 0, 0, 0, 0);

  i--;
  
  tt_srpt = ifofile->tt_srpt;
  vtsnum  = tt_srpt->title[i].title_set_nr;
  ttnnum  = tt_srpt->title[i].vts_ttn;
     
  vtsfile = ifoOpen(dvd, vtsnum);
    
  if (!vtsfile)
    return Py_BuildValue("(iiiii)", 0, 0, 0, 0, 0);

  playtime = 0;
  
  if (vtsfile->vts_pgcit) {
    dvd_time_t *ttime;
    ttime = &vtsfile->vts_pgcit->pgci_srp[0].pgc->playback_time;
    playtime = ((ttime->hour * 60) + ttime->minute) * 60 + ttime->second;
  }

  // Number of Chapters, Number of Angles, Playback time, Num Audio tracks,
  // Num subtitles
  return Py_BuildValue("(iiiii)", tt_srpt->title[i].nr_of_ptts,
                       tt_srpt->title[i].nr_of_angles, playtime,                
       
                       vtsfile->vtsi_mat->nr_of_vts_audio_streams,
                       vtsfile->vtsi_mat->nr_of_vts_subp_streams);
}


static PyObject * ifoinfo_get_subtitle_tracks(PyObject *self, PyObject *args) {
  char language[5];
  int trackno;
  tt_srpt_t *tt_srpt;
  int vtsnum, ttnnum;
  ifo_handle_t *vtsfile;
  int i;
  subp_attr_t *attr;
  
  if (!PyArg_ParseTuple(args, "ii", &i, &trackno))
    return Py_BuildValue("(s)", "N/A");

  i--;
  trackno--;
  
  tt_srpt = ifofile->tt_srpt;
  vtsnum = tt_srpt->title[i].title_set_nr;
  ttnnum = tt_srpt->title[i].vts_ttn;
  
  vtsfile = ifoOpen(dvd, vtsnum);
  
  if (vtsfile->vts_pgcit) {
    attr = &vtsfile->vtsi_mat->vts_subp_attr[trackno];

    if ( attr->type == 0
         && attr->lang_code == 0
         && attr->zero1 == 0
         && attr->zero2 == 0
         && attr->lang_extension == 0 ) {
      return Py_BuildValue("(s)", "N/A");
    }

    /* language code */
    if (isalpha((int)(attr->lang_code >> 8)) && isalpha((int)(attr->lang_code & 
0xff))) {
      snprintf(language, 5, "%c%c", attr->lang_code >> 8, attr->lang_code & 
0xff);
    } else {
      snprintf(language, 5, "%02x%02x", 0xff & (unsigned)(attr->lang_code >> 
8), 
               0xff & (unsigned)(attr->lang_code & 0xff));
    }

    return Py_BuildValue("(s)", language);
  }
}


static PyObject * ifoinfo_get_audio_tracks(PyObject *self, PyObject *args) {
  char audioformat[10];
  char audiolang[5];
  int audiochannels;
  int audioid;
  int audiofreq;
  audio_attr_t *attr;
  int i;
  int trackno;
  tt_srpt_t *tt_srpt;
  int vtsnum, ttnnum;
  ifo_handle_t *vtsfile;
  
  if (!PyArg_ParseTuple(args, "ii", &i, &trackno))
    return Py_BuildValue("i", 0);

  i--;
  trackno--;
  
  tt_srpt = ifofile->tt_srpt;
  vtsnum = tt_srpt->title[i].title_set_nr;
  ttnnum = tt_srpt->title[i].vts_ttn;
  
  vtsfile = ifoOpen(dvd, vtsnum);
  
  if (vtsfile->vts_pgcit && vtsfile->vtsi_mat) {
    attr = &vtsfile->vtsi_mat->vts_audio_attr[trackno];

    audioid = trackno + 128;
    
    if ( attr->audio_format == 0
         && attr->multichannel_extension == 0
         && attr->lang_type == 0
         && attr->application_mode == 0
         && attr->quantization == 0
         && attr->sample_frequency == 0
         && attr->channels == 0
         && attr->lang_extension == 0
         && attr->unknown1 == 0
         && attr->unknown1 == 0) {
      snprintf(audioformat, 10, "Unknown");
      return Py_BuildValue("i", 0);
    }
    
    /* audio format */
    switch (attr->audio_format) {
    case 0:
      snprintf(audioformat, 10, "ac3");
      break;
    case 1:
      snprintf(audioformat, 10, "N/A");
      break;
    case 2:
      snprintf(audioformat, 10, "mpeg1");
      break;
    case 3:
      snprintf(audioformat, 10, "mpeg2ext");
    break;
    case 4:
      snprintf(audioformat, 10, "lpcm");
      break;
    case 5:
      snprintf(audioformat, 10, "N/A");
      break;
    case 6:
      snprintf(audioformat, 10, "dts");
      break;
    default:
      snprintf(audioformat, 10, "N/A");
    }
    
    switch (attr->lang_type) {
    case 0:
      assert(attr->lang_code == 0 || attr->lang_code == 0xffff);
      snprintf(audiolang, 5, "N/A");
      break;
    case 1:
      snprintf(audiolang, 5, "%c%c", attr->lang_code>>8, attr->lang_code & 
0xff);
      break;
    default:
      snprintf(audiolang, 5, "N/A");
    }
    
    switch(attr->sample_frequency) {
    case 0:
      audiofreq = 48;
      break;
    case 1:
      audiofreq = -1;
      break;
    default:
      audiofreq = -1;
    }
    
    audiochannels = attr->channels + 1;
    
    //AUDIOTRACK: ID=%i; LANG=%s; FORMAT=%s; CHANNELS=%i; FREQ=%ikHz
    return Py_BuildValue("(issii)", audioid, audiolang, audioformat, 
audiochannels,
                         audiofreq);
  }

  return NULL;
}


--- NEW FILE: lsdvd.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# lsdvdinfo.py - parse dvd title structure using lsdvd
# -----------------------------------------------------------------------------
# $Id: lsdvd.py,v 1.1 2005/07/02 16:33:11 dischi Exp $
#
# TODO: update the ifomodule and remove the lsdvd parser
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# python imports
import os
import popen2
import logging
import re

# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
from discinfo import DiscInfo

# get logging object
log = logging.getLogger('metadata')

LSDVD_EXE='lsdvd'

class DVDAudio(mediainfo.AudioInfo):
    def __init__(self, data):
        mediainfo.AudioInfo.__init__(self)
        self.number   = int(data[1])
        if data[3] != 'xx':
            self.language = data[3]
            try:
                # some DVDs have a very bad language setting
                self.language.encode()
            except UnicodeError:
                self.language = ''

        try:
            self.codec = data[7]
            try:
                self.samplerate = int(data[9])
            except ValueError, e:
                if data[9].lower().find('khz') > 0:
                    pos = data[9].lower().find('khz')
                    self.samplerate = int(data[9][:pos]) * 1000
                else:
                    raise e
            self.channels = data[13]
        except Exception, e:
            # WTF, strange DVD, now try to find the bug (may not work)
            self.codec = data[data.index('Format:') + 1]
            try:
                freq = data[data.index('Frequency:') + 1]
                self.samplerate = int(freq)
            except ValueError:
                if freq.lower().find('khz') > 0:
                    self.samplerate = int(freq[:freq.lower().find('khz')])*1000
            self.channels = int(data[data.index('Channels:') + 1])


class DVDVideo(mediainfo.VideoInfo):
    def __init__(self, data):
        mediainfo.VideoInfo.__init__(self)
        self.width  = int(data[12])
        self.height = int(data[14])
        self.fps    = float(data[5])
        self.aspect = data[10]


class DVDTitle(mediainfo.AVInfo):
    def __init__(self, data):
        mediainfo.AVInfo.__init__(self)
        self.number = int(data[1])

        self.keys.append('subtitles')
        self.keys.append('chapters')

        self.mime = 'video/mpeg'

        l = re.split('[:.]', data[3])
        self.length   = (int(l[0])*60+int(l[1]))*60+int(l[2])
        self.trackno  = int(data[1])
        self.chapters = int(data[5])


class DVDInfo(DiscInfo):
    def __init__(self, device):
        DiscInfo.__init__(self)
        self.context = 'video'
        self.offset = 0

        log.info('trying lsdvd for scanning the disc')

        if os.path.isdir(device):
            self.parseDVDdir(device)
        else:
            self.parseDisc(device)

        self.keys.append('length')
        self.length = 0
        first       = 0

        for t in self.tracks:
            self.length += t.length
            if not first:
                first = t.length

        if self.length/len(self.tracks) == first:
            # badly mastered dvd
            self.length = first

        log.info('lsdvd detection ok')

        self.mime    = 'video/dvd'
        self.type    = 'DVD'
        self.subtype = 'video'


    def lsdvd(self, path):
        """
        use lsdvd to get informations about this disc
        """
        child = popen2.Popen3('%s -v -n -a -s "%s"' % \
                              (LSDVD_EXE, path), 1, 100)
        for line in child.fromchild.readlines():
            data = line.replace(',', '').replace('\t', '').\
                   replace('\n', '').lstrip(' ').split(' ')
            if len(data) > 2:
                if data[0] == 'Title:':
                    ti = DVDTitle(data)
                    self.appendtrack(ti)
                elif data[0] == 'Audio:':
                    self.tracks[-1].audio.append(DVDAudio(data))
                elif data[0] == 'Subtitle:':
                    self.tracks[-1].subtitles.append(data[3])
                elif data[0] == 'VTS:':
                    self.tracks[-1].video.append(DVDVideo(data))
                    self.tracks[-1].video[-1].length = self.tracks[-1].length
                elif data[:3] == ['Number', 'of', 'Angles:']:
                    self.tracks[-1].angles = int(data[3])
                    self.tracks[-1].keys.append('angles')

        child.wait()
        child.fromchild.close()
        child.childerr.close()
        child.tochild.close()

        if len(self.tracks) == 0:
            raise mediainfo.KaaMetadataParseError()

        for ti in self.tracks:
            ti.trackof = len(self.tracks)


    def parseDVDdir(self, dirname):
        if os.path.isdir(dirname+'/VIDEO_TS') or \
               os.path.isdir(dirname+'/video_ts') or \
               os.path.isdir(dirname+'/Video_ts'):
            self.lsdvd(dirname)
            return
        raise mediainfo.KaaMetadataParseError()


    def parseDisc(self, device):
        if DiscInfo.isDisc(self, device) != 2:
            raise mediainfo.KaaMetadataParseError()

        # brute force reading of the device to find out if it is a DVD
        f = open(device,'rb')
        f.seek(32768, 0)
        buffer = f.read(60000)

        if buffer.find('UDF') == -1:
            f.close()
            raise mediainfo.KaaMetadataParseError()

        # seems to be a DVD, read a little bit more
        buffer += f.read(550000)
        f.close()

        if buffer.find('VIDEO_TS') == -1 and \
               buffer.find('VIDEO_TS.IFO') == -1 and \
               buffer.find('OSTA UDF Compliant') == -1:
            raise mediainfo.KaaMetadataParseError()

        try:
            self.lsdvd(device)
        except mediainfo.KaaMetadataParseError:
            # we are very sure this is a DVD, maybe the drive was not
            # ready, let's try again
            self.lsdvd(device)


if os.environ.has_key('LSDVD') and os.environ['LSDVD']:
    LSDVD_EXE = os.environ['LSDVD']
else:
    for path in os.environ['PATH'].split(':'):
        if os.path.isfile(os.path.join(path, 'lsdvd')):
            LSDVD_EXE = os.path.join(path, 'lsdvd')
            break
    else:
        log.info('ImportError: lsdvd not found')
        raise ImportError

factory.register( 'video/dvd', mediainfo.EXTENSION_DEVICE,
                       mediainfo.TYPE_AV, DVDInfo )
factory.register( 'video/dvd', mediainfo.EXTENSION_DIRECTORY,
                       mediainfo.TYPE_AV, DVDInfo )

--- NEW FILE: CDDB.py ---
#!/usr/bin/env python

# Module for retrieving CDDB v1 data from CDDB servers via HTTP

# Written 17 Nov 1999 by Ben Gertzfield <[EMAIL PROTECTED]>
# This work is released under the GNU GPL, version 2 or later.

# Release version 1.3
# CVS ID: $Id: CDDB.py,v 1.1 2005/07/02 16:33:11 dischi Exp $

import urllib, string, socket, os, struct, re

name = 'CDDB.py'
version = 1.3

if os.environ.has_key('EMAIL'):
    (default_user, hostname) = string.split(os.environ['EMAIL'], '@')
else:
    try:
        default_user = os.geteuid() or os.environ['USER'] or 'user'
    except:
        default_user = 'user'
    hostname = socket.gethostname() or 'host'

proto = 4
default_server = 'http://freedb.freedb.org/~cddb/cddb.cgi'

def query(track_info, server_url=default_server,
          user=default_user, host=hostname, client_name=name,
          client_version=version, trying=0):

    disc_id = track_info[0]
    num_tracks = track_info[1]

    query_str = (('%08lx %d ') % (long(disc_id), num_tracks))

    for i in track_info[2:]:
        query_str = query_str + ('%d ' % i)
        
    query_str = urllib.quote_plus(string.rstrip(query_str))

    url = "%s?cmd=cddb+query+%s&hello=%s+%s+%s+%s&proto=%i" % \
          (server_url, query_str, user, host, client_name,
           client_version, proto)

    response = urllib.urlopen(url)
    
    # Four elements in header: status, category, disc-id, title
    header = string.split(string.rstrip(response.readline()), ' ', 3)

    try:
        header[0] = string.atoi(header[0])
    except:
        if trying > 10:
            return [ 900, None ]
        return query(track_info, default_server,
                     default_user, hostname, name, version, trying+1)

    if header[0] == 200:                # OK
        result = { 'category': header[1], 'disc_id': header[2], 'title':
                   header[3] }

        return [ header[0], result ]

    elif header[0] == 211 or header[0] == 210: # multiple matches
        result = []

        for line in response.readlines():
            line = string.rstrip(line)

            if line == '.':             # end of matches
                break
                                        # otherwise:
                                        # split into 3 pieces, not 4
                                        # (thanks to bgp for the fix!)
            match = string.split(line, ' ', 2)

            result.append({ 'category': match[0], 'disc_id': match[1], 'title':
                            match[2] })

        return [ header[0], result ]

    else:
        return [ header[0], None ]

def read(category, disc_id, server_url=default_server, 
         user=default_user, host=hostname, client_name=name,
         client_version=version, trying=0):

    url = "%s?cmd=cddb+read+%s+%s&hello=%s+%s+%s+%s&proto=%i" % \
          (server_url, category, disc_id, user, host, client_name,
           client_version, proto)

    response = urllib.urlopen(url)
    
    header = string.split(string.rstrip(response.readline()), ' ', 3)

    try:
        header[0] = string.atoi(header[0])
    except:
        if trying > 10:
            return [ 900, None ]
        return read(category, disc_id, default_server, 
                    user, host, client_name, client_version, trying+1)

    if header[0] == 210 or header[0] == 417: # success or access denied
        reply = []

        for line in response.readlines():
            line = string.rstrip(line)

            if line == '.':
                break;

            line = string.replace(line, r'\t', "\t")
            line = string.replace(line, r'\n', "\n")
            line = string.replace(line, r'\\', "\\")

            reply.append(line)

        if header[0] == 210:            # success, parse the reply
            return [ header[0], parse_read_reply(reply) ]
        else:                           # access denied. :(
            return [ header[0], reply ]
    else:
        return [ header[0], None ]

def parse_read_reply(comments):
    
    len_re = re.compile(r'#\s*Disc length:\s*(\d+)\s*seconds')
    revis_re = re.compile(r'#\s*Revision:\s*(\d+)')
    submit_re = re.compile(r'#\s*Submitted via:\s*(.+)')
    keyword_re = re.compile(r'([^=]+)=(.*)')

    result = {}

    for line in comments:
        keyword_match = keyword_re.match(line)
        if keyword_match:
            (keyword, data) = keyword_match.groups()

            if result.has_key(keyword):
                result[keyword] = result[keyword] + data
            else:
                result[keyword] = data
            continue

        len_match = len_re.match(line)
        if len_match:
            result['disc_len'] = int(len_match.group(1))
            continue

        revis_match = revis_re.match(line)
        if revis_match:
            result['revision'] = int(revis_match.group(1))
            continue

        submit_match = submit_re.match(line)
        if submit_match:
            result['submitted_via'] = submit_match.group(1)
            continue

    return result

--- NEW FILE: datainfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# datainfo.py - info about a normal data disc
# -----------------------------------------------------------------------------
# $Id: datainfo.py,v 1.1 2005/07/02 16:33:11 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
from discinfo import DiscInfo

class DataDiscInfo(DiscInfo):
    def __init__(self,device):
        DiscInfo.__init__(self)
        if DiscInfo.isDisc(self, device) != 2:
            raise mediainfo.KaaMetadataParseError()
        self.context = 'unknown'
        self.offset = 0
        self.mime = 'unknown/unknown'
        self.type = 'CD'
        self.subtype = 'data'


factory.register( 'cd/unknown', mediainfo.EXTENSION_DEVICE,
                       mediainfo.TYPE_NONE, DataDiscInfo )

--- NEW FILE: audioinfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# audioinfo.py - support for audio cds
# -----------------------------------------------------------------------------
# $Id: audioinfo.py,v 1.1 2005/07/02 16:33:11 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# python imports
import cdrom
import logging

# kaa imports
import kaa.metadata
from kaa.metadata import mediainfo
from kaa.metadata import factory

# disc imports
import discinfo
import DiscID
import CDDB

# get logging object
log = logging.getLogger('metadata')

class AudioDiscInfo(discinfo.DiscInfo):
    def __init__(self,device):
        discinfo.DiscInfo.__init__(self)
        self.context = 'audio'
        self.offset = 0
        # check disc
        if discinfo.DiscInfo.isDisc(self, device) != 1:
            raise mediainfo.KaaMetadataParseError()

        self.query(device)
        self.mime = 'audio/cd'
        self.type = 'CD'
        self.subtype = 'audio'


    def query(self, device):

        disc_id = DiscID.disc_id(device)
        if kaa.metadata.USE_NETWORK:
            try:
                (query_stat, query_info) = CDDB.query(disc_id)
            except:
                # Oops no connection
                query_stat = 404
        else:
            query_stat = 404

        if query_stat == 210 or query_stat == 211:
            # set this to success
            query_stat = 200

            for i in query_info:
                if i['title'] != i['title'].upper():
                    query_info = i
                    break
            else:
                query_info = query_info[0]

        elif query_stat != 200:
            log.error("failure getting disc info, status %i" % query_stat)

        if query_stat == 200:
            qi = query_info['title'].split('/')
            self.artist = qi[0].strip()
            self.title = qi[1].strip()
            for type in ('title', 'artist'):
                if getattr(self, type) and \
                       getattr(self, type)[0] in ('"', '\'') \
                       and getattr(self, type)[-1] in ('"', '\''):
                    setattr(self, type, getattr(self, type)[1:-1])
            (read_stat, read_info) = CDDB.read(query_info['category'],
                                               query_info['disc_id'])
            # id = disc_id + number of tracks
            #self.id = '%s_%s' % (query_info['disc_id'], disc_id[1])

            if read_stat == 210:
                for i in range(0, disc_id[1]):
                    mi = mediainfo.MusicInfo()
                    mi.title = read_info['TTITLE' + `i`]
                    mi.album = self.title
                    mi.artist = self.artist
                    mi.genre = query_info['category']
                    mi.codec = 'PCM'
                    mi.samplerate = 44.1
                    mi.trackno = i+1
                    mi.trackof = disc_id[1]
                    self.tracks.append(mi)
                    for type in ('title', 'album', 'artist', 'genre'):
                        if getattr(mi, type) and \
                               getattr(mi, type)[0] in ('"', '\'') \
                           and getattr(mi, type)[-1] in ('"', '\''):
                            setattr(mi, type, getattr(mi, type)[1:-1])
            else:
                log.error("failure getting track info, status: %i" % read_stat)
                # set query_stat to somthing != 200
                query_stat = 400


        if query_stat != 200:
            log.error("failure getting disc info, status %i" % query_stat)
            self.no_caching = 1
            for i in range(0, disc_id[1]):
                mi = mediainfo.MusicInfo()
                mi.title = 'Track %s' % (i+1)
                mi.codec = 'PCM'
                mi.samplerate = 44.1
                mi.trackno = i+1
                mi.trackof = disc_id[1]
                self.tracks.append(mi)


        # read the tracks to generate the title list
        device = open(device)
        (first, last) = cdrom.toc_header(device)

        lmin = 0
        lsec = 0

        num = 0
        for i in range(first, last + 2):
            if i == last + 1:
                min, sec, frames = cdrom.leadout(device)
            else:
                min, sec, frames = cdrom.toc_entry(device, i)
            if num:
                self.tracks[num-1].length = (min-lmin)*60 + (sec-lsec)
            num += 1
            lmin, lsec = min, sec
        device.close()

        # correct bad titles for the tracks, containing also the artist
        for t in self.tracks:
            if not self.artist or not t.title.startswith(self.artist):
                break
        else:
            for t in self.tracks:
                t.title = t.title[len(self.artist):].lstrip('/ \t-_')

        # correct bad titles for the tracks, containing also the title
        for t in self.tracks:
            if not self.title or not t.title.startswith(self.title):
                break
        else:
            for t in self.tracks:
                t.title = t.title[len(self.title):].lstrip('/ \t-_')



factory.register( 'audio/cd', mediainfo.EXTENSION_DEVICE,
                       mediainfo.TYPE_AUDIO, AudioDiscInfo )

--- NEW FILE: DiscID.py ---
#!/usr/bin/env python

# Module for fetching information about an audio compact disc and
# returning it in a format friendly to CDDB.

# If called from the command line, will print out disc info in a
# format identical to Robert Woodcock's 'cd-discid' program.

# Written 17 Nov 1999 by Ben Gertzfield <[EMAIL PROTECTED]>
# This work is released under the GNU GPL, version 2 or later.

# Release version 1.3


# changes for kaa.metadata:
#
# Fixed bug in the cdrommodule that the file was not closed after usage.
# The result was a drive you can't eject while the program (e.g. Freevo)
# is running. Added cvs log for DiscID and cdrommodule to keep track of
# all changes we did for kaa.metadata.
#

try:
    import cdrom, sys
except ImportError:
    # Seems cdrom has either not been compiler or is not supported
    # on this System
    pass
    

def cddb_sum(n):
    ret = 0
    
    while n > 0:
        ret = ret + (n % 10)
        n = n / 10

    return ret

def disc_id(device):
    device = cdrom.open(device)
    (first, last) = cdrom.toc_header(device)

    track_frames = []
    checksum = 0
    
    for i in range(first, last + 1):
        (min, sec, frame) = cdrom.toc_entry(device, i)
        checksum = checksum + cddb_sum(min*60 + sec)
        track_frames.append(min*60*75 + sec*75 + frame)

    (min, sec, frame) = cdrom.leadout(device)
    track_frames.append(min*60*75 + sec*75 + frame)

    total_time = (track_frames[-1] / 75) - (track_frames[0] / 75)
               
    discid = ((long(checksum) % 0xff) << 24 | total_time << 8 | last)
    cdrom.close(device)
    return [discid, last] + track_frames[:-1] + [ track_frames[-1] / 75 ]

if __name__ == '__main__':

    dev_name = None
    device = None
    
    if len(sys.argv) >= 2:
        dev_name = sys.argv[1]

    if dev_name:
        device = open(dev_name)
    else:
        device = open()
        
    disc_info = disc_id(device)

    print ('%08lx' % disc_info[0]),

    for i in disc_info[1:]:
        print ('%d' % i),



-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to