1# Copyright (C) 2009-2010 Aren Olson 2# 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 2, or (at your option) 6# any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16# 17# 18# The developers of the Exaile media player hereby grant permission 19# for non-GPL compatible GStreamer and Exaile plugins to be used and 20# distributed together with GStreamer and Exaile. This permission is 21# above and beyond the permissions granted by the GPL license by which 22# Exaile is covered. If you modify this code, you may extend this 23# exception to your version of the code, but you are not obligated to 24# do so. If you do not wish to do so, delete this exception statement 25# from your version. 26 27import logging 28import threading 29import os 30 31from gi.repository import GLib 32 33from xl import common, event, formatter, settings, transcoder, trax 34from xl.nls import gettext as _ 35from xlgui.panel import device 36import xlgui 37 38 39logger = logging.getLogger(__name__) 40 41 42class CDImportThread(common.ProgressThread): 43 def __init__(self, cd_importer): 44 common.ProgressThread.__init__(self) 45 46 self.cd_importer = cd_importer 47 48 def stop(self): 49 """ 50 Stops the thread 51 """ 52 self.cd_importer.stop() 53 common.ProgressThread.stop(self) 54 55 def on_progress_update(self, progress=None): 56 """ 57 Notifies about progress changes 58 """ 59 if progress is None: 60 progress = self.cd_importer.get_progress() * 100 61 62 self.emit('progress-update', int(progress)) 63 64 return True 65 66 def run(self): 67 """ 68 Runs the thread 69 """ 70 progress_id = GLib.timeout_add_seconds(1, self.on_progress_update) 71 self.cd_importer.do_import() 72 GLib.source_remove(progress_id) 73 self.emit('done') 74 75 76class CDPanel(device.FlatPlaylistDevicePanel): 77 def __init__(self, *args): 78 device.FlatPlaylistDevicePanel.__init__(self, *args) 79 self.__importing = False 80 81 event.add_ui_callback(self._tree_queue_draw, 'cddb_info_retrieved') 82 83 def _tree_queue_draw(self, type, cdplaylist, object=None): 84 if not hasattr(self.fppanel, 'tree'): 85 return 86 87 if cdplaylist in self.device.playlists: 88 logger.info("Calling queue_draw for %s", str(cdplaylist)) 89 self.fppanel.tree.queue_draw() 90 91 def do_import(self, tracks): 92 if self.__importing: 93 return 94 self.__importing = True 95 cd_importer = CDImporter(tracks) 96 thread = CDImportThread(cd_importer) 97 thread.connect('done', lambda *e: self._import_finish()) 98 self.main.controller.progress_manager.add_monitor( 99 thread, _("Importing CD..."), 'drive-optical' 100 ) 101 102 def _import_finish(self): 103 self.__importing = False 104 105 106class CDImporter: 107 def __init__(self, tracks): 108 self.tracks = [t for t in tracks if t.get_loc_for_io().startswith("cdda")] 109 self.duration = sum(t.get_tag_raw('__length') for t in self.tracks) 110 self.formatter = formatter.TrackFormatter( 111 settings.get_option( 112 "cd_import/outpath", 113 "%s/$artist/$album/$tracknumber - $title" % os.getenv("HOME"), 114 ) 115 ) 116 self.current = None 117 self.current_len = None 118 self.progress = 0.0 119 120 self.running = False 121 122 self.cont = None 123 self.__prepare_transcoder() 124 125 def __prepare_transcoder(self): 126 formats = transcoder.get_formats() 127 default_format = next(formats.keys()) 128 self.format = settings.get_option("cd_import/format", default_format) 129 default_quality = formats[default_format]['default'] 130 self.quality = settings.get_option("cd_import/quality", default_quality) 131 self.transcoder = transcoder.Transcoder( 132 self.format, self.quality, self._error_cb, self._end_cb 133 ) 134 135 def do_import(self): 136 self.running = True 137 138 self.cont = threading.Event() 139 140 self.transcoder.set_format(self.format) 141 if self.quality != -1: 142 self.transcoder.set_quality(self.quality) 143 144 for tr in self.tracks: 145 self.cont.clear() 146 self.current = tr 147 self.current_len = tr.get_tag_raw('__length') 148 loc = tr.get_loc_for_io() 149 trackno, device = loc[7:].split("/#") 150 src = "cdparanoiasrc track=%s device=\"%s\"" % (trackno, device) 151 self.transcoder.set_raw_input(src) 152 outloc = self.get_output_location(tr) 153 self.transcoder.set_output(outloc) 154 self.transcoder.start_transcode() 155 self.cont.wait() 156 if not self.running: 157 break 158 tr2 = trax.Track("file://" + outloc) 159 ntags = { 160 t: tr.get_tag_raw(t) for t in tr.list_tags() if not t.startswith("__") 161 } 162 tr2.set_tags(**ntags) 163 tr2.write_tags() 164 try: 165 incr = tr.get_tag_raw('__length') / self.duration 166 self.progress += incr 167 except Exception: 168 raise 169 self.progress = 100.0 170 171 def _end_cb(self): 172 self.cont.set() 173 xlgui.main.mainwindow().message.show_info("Finished transcoding files") 174 175 def _error_cb(self, gerror, message_string): 176 self.running = False 177 xlgui.main.mainwindow().message.show_error( 178 _("Error transcoding files from CD."), "%s" % gerror.message.encode() 179 ) 180 181 def get_output_location(self, track): 182 path = self.formatter.format(track) 183 directorypath = os.path.dirname(path) 184 185 if not os.path.exists(directorypath): 186 os.makedirs(directorypath) 187 188 extension = transcoder.FORMATS[self.transcoder.dest_format]['extension'] 189 190 return path + '.' + extension 191 192 def stop(self): 193 self.running = False 194 self.transcoder.stop() 195 196 def get_progress(self): 197 if not self.current or not self.current_len: 198 return self.progress 199 incr = self.current_len / self.duration 200 pos = self.transcoder.get_time() / self.current_len 201 return self.progress + pos * incr 202