# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <EXPERIMENTAL>
# a backend to expose to the local network the content of an audio CD
# inserted in a local drive
# the CD data is extracted from the CDDB/FreeDB database
# Warning: this backend does not detect insertion and ejection of CD drive
# the CD must be inserted before activating the backing.
# The backend must be disabled before ejecting the audio CD.
# Dependencies
# CDDB.py and DiscID.py (ex: debian package python-cddb)
# python-gst (with base and ugly plugins)
# TODO: switch from CDDB/FreeDB to musicBrainz
# TODO: find source for AlbumArt UI
# TODO: support other character encoding environment than ISO-8856-1
# Copyright 2010, Jean-Michel Sizun
try:
import CDDB
except ImportError:
CDDB = None
try:
import DiscID
except ImportError:
DiscID = None
from functools import cmp_to_key
from twisted.internet import reactor, threads
from coherence.backend import AbstractBackendStore, Container, BackendItem
from coherence.transcoder import GStreamerPipeline
from coherence.upnp.core import DIDLLite
PLAY_TRACK_GST_PIPELINE = 'cdiocddasrc device=%s track=%d ! wavenc name=enc'
TRACK_MIMETYPE = 'audio/x-wav'
TRACK_FOURTH_FIELD = '*'
[docs]class TrackItem(BackendItem):
logCategory = 'audiocd'
def __init__(self, device_name='/dev/cdrom', track_number=1,
artist='Unknown', title='Unknown'):
BackendItem.__init__(self)
self.device_name = device_name
self.track_number = track_number
self.artist = artist
self.title = title
self.size = None
self.mimetype = TRACK_MIMETYPE
self.fourth_field = TRACK_FOURTH_FIELD
self.item = None
self.parent = None
self.pipeline = PLAY_TRACK_GST_PIPELINE % (
self.device_name, self.track_number)
self.location = GStreamerPipeline(self.pipeline, self.mimetype)
[docs] def get_item(self):
if self.item is None:
upnp_id = self.storage_id
upnp_parent_id = self.parent.get_id()
url = self.store.urlbase + str(self.storage_id)
self.item = DIDLLite.MusicTrack(
upnp_id, upnp_parent_id, self.title)
res = DIDLLite.Resource(
url, f'http-get:*:{self.mimetype}:{self.fourth_field}')
# res.duration = self.duration
# res.size = self.get_size()
self.item.res.append(res)
return self.item
[docs] def get_name(self):
return self.title
[docs] def get_path(self):
return self.location
[docs] def get_size(self):
return self.size
[docs] def get_id(self):
return self.storage_id
[docs]class AudioCDStore(AbstractBackendStore):
logCategory = 'audiocd'
implements = ['MediaServer']
description = ('audioCD', '', None)
options = [
{'option': 'version', 'text': 'UPnP Version:', 'type': 'int',
'default': 2, 'enum': (2, 1),
'help': 'the highest UPnP version this MediaServer shall support',
'level': 'advance'},
{'option': 'uuid', 'text': 'UUID Identifier:', 'type': 'string',
'help': 'the unique (UPnP) identifier for this MediaServer, '
'usually automatically set',
'level': 'advance'},
{'option': 'device_name', 'text': 'device name for audio CD:',
'type': 'string',
'help': 'device name containing the audio cd.'}
]
disc_title = None
cdrom = None
def __init__(self, server, **kwargs):
AbstractBackendStore.__init__(self, server, **kwargs)
self.name = 'audio CD'
self.device_name = kwargs.get('device_name', '/dev/cdom')
threads.deferToThread(self.extractAudioCdInfo)
# will be fired when the audio CD info is extracted
# self.init_completed()
[docs] def upnp_init(self):
self.current_connection_id = None
if self.server:
self.server.connection_manager_server.set_variable(
0, 'SourceProtocolInfo',
[f'http-get:*:{TRACK_MIMETYPE}:{TRACK_FOURTH_FIELD}'],
default=True)
self.server.content_directory_server.set_variable(
0, 'SystemUpdateID', self.update_id)
# self.server.content_directory_server.set_variable(
# 0, 'SortCapabilities', '*')
[docs] def checkIfAudioCdStillPresent(self):
try:
disc_id = DiscID.disc_id(self.cdrom)
reactor.callLater(2, self.checkIfAudioCdStillPresent)
except Exception:
self.warning(f'audio CD {self.disc_title} ejected:'
f' closing UPnP server!')
self.server.coherence.remove_plugin(self.server)
def __repr__(self):
return self.__class__.__name__