root/branches/0.3/src/cdrom_control.py

Revision 823, 7.9 kB (checked in by nicfit, 1 year ago)

Fixed TB

Line 
1 ################################################################################
2 #  Copyright (C) 2006  Travis Shirk <travis@pobox.com>
3 #
4 #  This program is free software; you can redistribute it and/or modify
5 #  it under the terms of the GNU General Public License as published by
6 #  the Free Software Foundation; either version 2 of the License, or
7 #  (at your option) any later version.
8 #
9 #  This program is distributed in the hope that it will be useful,
10 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #  GNU General Public License for more details.
13 #
14 #  You should have received a copy of the GNU General Public License
15 #  along with this program; if not, write to the Free Software
16 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 #
18 #  $Id$
19 ################################################################################
20 import os
21 from fcntl import ioctl
22 import gobject, gtk, gnomevfs
23
24 from mesk import MeskException
25 import mesk.playlist
26 from mesk.i18n import _
27 from mesk.audio.source import AudioMetaData
28 from mesk.audio.cdaudio import CDAudioSource
29 from playlist_control import PlaylistControl
30 import devices, dialogs
31
32 class CDROMControl(PlaylistControl):
33
34     def __init__(self, hal_udi, status_bar, **keywords):
35         PlaylistControl.__init__(self, hal_udi, status_bar)
36         self.hal_udi = hal_udi
37
38         self.device_manager = devices.get_mgr()
39         self.device = self.device_manager.get_optical_devices()[self.hal_udi]
40         self.block_device = self.device.dev.GetProperty('block.device')
41         display_name = self.device_manager.get_device_display_name(self.device)
42         self.name = display_name
43
44         # Check capabilities
45         if not devices.cdrom_check_capablities(self.block_device):
46             raise MeskException(_('<b>Unsupported CDROM device.</b>') ,
47                                 _("CD device %s does not support open, close "
48                                   "and/or media changed."))
49
50         # Setup tab label
51         tab_label = self.tab_label_xml.get_widget('playlist_tab_label')
52         tab_label.set_text(display_name)
53         tab_label_img = self.tab_label_xml.get_widget('playlist_tab_image')
54         tab_label_img.set_from_stock(gtk.STOCK_CDROM, gtk.ICON_SIZE_BUTTON)
55
56         # Add buttons
57         button_box = self.widget_xml.get_widget('playlist_buttons_hbox')
58         eject_button = gtk.Button(label=None)
59         eject_button.set_image(gtk.image_new_from_stock(gtk.STOCK_REMOVE,
60                                                         gtk.ICON_SIZE_BUTTON))
61         self._tooltips = gtk.Tooltips()
62         self._tooltips.set_tip(eject_button, _('Eject'))
63         button_box.add(eject_button)
64         eject_button.show()
65         eject_button.connect('clicked', self._eject)
66
67         self._read_cd()
68
69     def _supports_properties(self):
70         return False
71
72     def _read_cd(self):
73         # Read CD prompting for insert if necessary
74         pl = None
75         while not pl:
76             status = devices.cdrom_disc_status(self.block_device)
77             if status != devices.CD_STATUS_AUDIO:
78                 # No audio CD found, eject
79                 try:
80                     devices.cdrom_eject(self.block_device)
81                 except IOError, ex:
82                     # The device may be busy
83                     raise MeskException(_('<b>Unable to eject CD</b>'),
84                                         _("Another application is using the "
85                                           "CDROM device (%s).\nTry again when "
86                                           "the device is not busy.") %
87                                         self.block_device)
88
89                 # Prompt for CD insert
90                 d = dialogs.ConfirmationDialog(self.widget.get_parent_window())
91                 d.set_markup(_('<b>Insert audio CD into %s</b>') %
92                              self.block_device)
93                 if d.confirm():
94                     devices.cdrom_close(self.block_device)
95                 else:
96                     raise mesk.MeskException(None)
97
98             else:
99                 pl = self._read_cdinfo()
100                 if pl.get_length() == 0:
101                     pl = None
102
103         assert(pl and pl.get_length())
104         self._set_playlist(pl)
105         self._set_read_only(True)
106
107     def _read_cdinfo(self):
108         import DiscID
109         pl = mesk.playlist.Playlist()
110         disc = DiscID.open(self.block_device)
111
112         try:
113             disc_info = DiscID.disc_id(disc)
114         except:
115             disc.close()
116             return pl
117         disc.close()
118
119         disc_id = disc_info[0]
120         num_tracks = disc_info[1]
121
122         # Create playlist from CD tracks
123         minus = 0
124         total = 0
125         for i in range(num_tracks):
126             length = (disc_info[i + 3] / 75) - minus
127             if i + 1 == disc_info[1]:
128                 length = disc_info[i + 3] - total
129
130             metadata = AudioMetaData()
131             metadata.time_secs = length
132             metadata.track_num = i + 1
133             pl.append(CDAudioSource(self.block_device, metadata.track_num,
134                                     metadata))
135
136             minus = disc_info[i + 3] / 75
137             total += length
138
139         # Fetch metadata from CDDB
140         self.cddb_fetcher = CDDBThread(disc_info, self._update_metadata_cddb)
141         self.cddb_fetcher.start()
142
143         return pl
144
145     ## Override base class methods
146     def shutdown(self):
147         mesk.log.debug("Shutting down CDROMControl %s" % self.block_device)
148         # Remove config section (added by base class)
149         self._pl_config.delete()
150
151
152     def is_playlist_saved(self):
153         return False
154     def _save_playlist(self, interval = 10000):
155         pass
156
157     def _process_tab_menu(self, menu_xml, menu):
158         eject_menuitem = gtk.MenuItem('_Eject', use_underline=True)
159         eject_menuitem.connect('activate', self._eject)
160         eject_menuitem.show()
161         menu.append(eject_menuitem)
162         # Base class impl
163         PlaylistControl._process_tab_menu(self, menu_xml, menu)
164
165     def _eject(self, widget):
166         mesk.log.debug("Ejecting %s" % self.block_device)
167         devices.cdrom_eject(self.block_device)
168
169     def _update_metadata_cddb(self, disc_id, cddb_info):
170         num_tracks = disc_id[1]
171         encoding = 'iso8859-1'
172         if CDDB.proto >= 6:
173             encoding = 'utf-8'
174
175         artist, album = cddb_info['DTITLE'].split(' / ')
176         artist = artist.decode(encoding, 'replace')
177         album = album.decode(encoding, 'replace')
178         year = cddb_info['DYEAR']
179         for i in range(num_tracks):
180             src = self._playlist[i]
181             src.meta_data.artist = artist
182             src.meta_data.album = album
183             if year:
184                 src.meta_data.year = int(year)
185             src.meta_data.title = cddb_info['TTITLE%d' % i].decode(encoding,
186                                                                    'replace')
187             src.meta_data.frozen = True  # No further updates necessary
188             self._update_source_row(src)
189
190 import threading, CDDB
191 class CDDBThread(threading.Thread):
192     def __init__(self, disc_id, cb):
193         threading.Thread.__init__(self)
194         self._disc_id = disc_id
195         self._cb = cb
196
197     def run(self):
198         (status, info) = CDDB.query(self._disc_id,
199                                     client_name=mesk.info.APP_NAME,
200                                     client_version=mesk.info.APP_VERSION)
201         if status in [200, 210, 211]:
202             if status in [210, 211]:
203                 info = info[0]
204         else:
205             mesk.log.verbose('Unable to fetch CDDB info, status=%d' % status)
206
207         if not info:
208             mesk.log.verbose('No CDDB info')
209             return
210
211         (status, info) = CDDB.read(info['category'], info['disc_id'])
212         if status != 210:
213             mesk.log.verbose('Unable to fetch CDDB info, status=%d' % status)
214             return
215
216         gobject.idle_add(self._cb, self._disc_id, info)
Note: See TracBrowser for help on using the browser.