def get_feed(self, feed):
return self._gvoice.get_feed(feed)
- def download(self, messageId, adir):
+ def download(self, messageId, targetPath):
"""
Download a voicemail or recorded call MP3 matching the given ``msg``
which can either be a ``Message`` instance, or a SHA1 identifier.
- Saves files to ``adir`` (defaults to current directory).
Message hashes can be found in ``self.voicemail().messages`` for example.
Returns location of saved file.
"""
- return self._gvoice.download(messageId, adir)
+ self._gvoice.download(messageId, targetPath)
def is_valid_syntax(self, number):
"""
url = self._downloadVoicemailURL+messageId
return url
- def download(self, messageId, adir):
+ def download(self, messageId, targetPath):
"""
Download a voicemail or recorded call MP3 matching the given ``msg``
which can either be a ``Message`` instance, or a SHA1 identifier.
- Saves files to ``adir`` (defaults to current directory).
Message hashes can be found in ``self.voicemail().messages`` for example.
@returns location of saved file.
@blocks
"""
page = self._get_page(self.recording_url(messageId))
- fn = os.path.join(adir, '%s.mp3' % messageId)
- with open(fn, 'wb') as fo:
+ with open(targetPath, 'wb') as fo:
fo.write(page)
- return fn
def is_valid_syntax(self, number):
"""
self._session.draft.remove_contact(self._uiItems[index]["cid"])
+class VoicemailPlayer(object):
+
+ def __init__(self, app, session, errorLog):
+ self._app = app
+ self._session = session
+ self._errorLog = errorLog
+ self._session.voicemailAvailable.connect(self._on_voicemail_downloaded)
+ self._session.draft.recipientsChanged.connect(self._on_recipients_changed)
+
+ self._downloadButton = QtGui.QPushButton("Download Voicemail")
+ self._downloadButton.clicked.connect(self._on_voicemail_download)
+ self._downloadLayout = QtGui.QHBoxLayout()
+ self._downloadLayout.addWidget(self._downloadButton)
+ self._downloadWidget = QtGui.QWidget()
+ self._downloadWidget.setLayout(self._downloadLayout)
+
+ self._playLabel = QtGui.QLabel("Voicemail")
+ self._saveButton = QtGui.QPushButton("Save")
+ self._saveButton.clicked.connect(self._on_voicemail_save)
+ self._playerLayout = QtGui.QHBoxLayout()
+ self._playerLayout.addWidget(self._playLabel)
+ self._playerLayout.addWidget(self._saveButton)
+ self._playerWidget = QtGui.QWidget()
+ self._playerWidget.setLayout(self._playerLayout)
+
+ self._visibleWidget = None
+ self._layout = QtGui.QHBoxLayout()
+ self._layout.setContentsMargins(0, 0, 0, 0)
+ self._widget = QtGui.QWidget()
+ self._widget.setLayout(self._layout)
+ self._update_state()
+
+ @property
+ def toplevel(self):
+ return self._widget
+
+ def destroy(self):
+ self._session.voicemailAvailable.disconnect(self._on_voicemail_downloaded)
+ self._session.draft.recipientsChanged.disconnect(self._on_recipients_changed)
+
+ def _show_download(self, messageId):
+ if self._visibleWidget is self._downloadWidget:
+ return
+ self._hide()
+ self._layout.addWidget(self._downloadWidget)
+ self._visibleWidget = self._downloadWidget
+ self._visibleWidget.show()
+
+ def _show_player(self, messageId):
+ if self._visibleWidget is self._playerWidget:
+ return
+ self._hide()
+ self._layout.addWidget(self._playerWidget)
+ self._visibleWidget = self._playerWidget
+ self._visibleWidget.show()
+
+ def _hide(self):
+ if self._visibleWidget is None:
+ return
+ self._visibleWidget.hide()
+ self._layout.removeWidget(self._visibleWidget)
+ self._visibleWidget = None
+
+ def _update_state(self):
+ if self._session.draft.get_num_contacts() != 1:
+ self._hide()
+ return
+
+ (cid, ) = self._session.draft.get_contacts()
+ messageId = self._session.draft.get_message_id(cid)
+ if messageId is None:
+ self._hide()
+ return
+
+ if self._session.is_available(messageId):
+ self._show_player(messageId)
+ else:
+ self._show_download(messageId)
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_voicemail_save(self, arg):
+ with qui_utils.notify_error(self._app.errorLog):
+ targetPath = QtGui.QFileDialog.getSaveFileName(None, caption="Save Voicemail", filter="Audio File (*.mp3)")
+ targetPath = unicode(targetPath)
+ if not targetPath:
+ return
+
+ (cid, ) = self._session.draft.get_contacts()
+ messageId = self._session.draft.get_message_id(cid)
+ sourcePath = self._session.voicemail_path(messageId)
+ import shutil
+ shutil.copy2(sourcePath, targetPath)
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_voicemail_download(self, arg):
+ with qui_utils.notify_error(self._app.errorLog):
+ (cid, ) = self._session.draft.get_contacts()
+ messageId = self._session.draft.get_message_id(cid)
+ self._session.download_voicemail(messageId)
+ self._hide()
+
+ @QtCore.pyqtSlot()
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_recipients_changed(self):
+ with qui_utils.notify_error(self._app.errorLog):
+ self._update_state()
+
+ @QtCore.pyqtSlot(str, str)
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_voicemail_downloaded(self, messageId, filepath):
+ with qui_utils.notify_error(self._app.errorLog):
+ self._update_state()
+
+
class SMSEntryWindow(qwrappers.WindowWrapper):
MAX_CHAR = 160
self._history = QtGui.QLabel()
self._history.setTextFormat(QtCore.Qt.RichText)
self._history.setWordWrap(True)
+ self._voicemailPlayer = VoicemailPlayer(self._app, self._session, self._errorLog)
self._smsEntry = QtGui.QTextEdit()
self._smsEntry.textChanged.connect(self._on_letter_count_changed)
self._entryLayout = QtGui.QVBoxLayout()
self._entryLayout.addWidget(self._targetList.toplevel)
self._entryLayout.addWidget(self._history)
+ self._entryLayout.addWidget(self._voicemailPlayer.toplevel, 0)
self._entryLayout.addWidget(self._smsEntry)
self._entryLayout.setContentsMargins(0, 0, 0, 0)
self._entryWidget = QtGui.QWidget()
self._session.draft.called.disconnect(self._on_op_finished)
self._session.draft.cancelled.disconnect(self._on_op_finished)
self._session.draft.error.disconnect(self._on_op_error)
+ self._voicemailPlayer.destroy()
window = self._window
self._window = None
try:
@QtCore.pyqtSlot()
@misc_utils.log_exception(_moduleLogger)
def _on_refresh_history(self):
- draftContactsCount = self._session.draft.get_num_contacts()
- if draftContactsCount != 1:
- # Changing contact count will automatically refresh it
- return
- (cid, ) = self._session.draft.get_contacts()
- self._update_history(cid)
+ with qui_utils.notify_error(self._app.errorLog):
+ draftContactsCount = self._session.draft.get_num_contacts()
+ if draftContactsCount != 1:
+ # Changing contact count will automatically refresh it
+ return
+ (cid, ) = self._session.draft.get_contacts()
+ self._update_history(cid)
@QtCore.pyqtSlot()
@misc_utils.log_exception(_moduleLogger)
title = misc_utils.make_pretty(number)
description = misc_utils.make_pretty(number)
numbersWithDescriptions = [(number, "")]
- self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions)
+ self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions)
@QtCore.pyqtSlot()
@QtCore.pyqtSlot(bool)
description = misc_utils.make_pretty(number)
numbersWithDescriptions = [(number, "")]
self._session.draft.clear()
- self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions)
+ self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions)
self._session.draft.call()
descriptionRows.append("<tr><td>%s</td></tr>" % "</td><td>".join(rowItems))
description = "<table>%s</table>" % "".join(descriptionRows)
numbersWithDescriptions = [(str(contactDetails[QtCore.QString("number")]), "")]
- self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions)
+ self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions)
class Messages(object):
if not name:
name = "Unknown"
- contactId = str(contactDetails[QtCore.QString("id")])
+ if str(contactDetails[QtCore.QString("type")]) == "Voicemail":
+ messageId = str(contactDetails[QtCore.QString("id")])
+ else:
+ messageId = None
+ contactId = str(contactDetails[QtCore.QString("contactId")])
title = name
description = unicode(contactDetails[QtCore.QString("expandedMessages")])
numbersWithDescriptions = [(number, "")]
- self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions)
+ self._session.draft.add_contact(contactId, messageId, title, description, numbersWithDescriptions)
@QtCore.pyqtSlot(QtCore.QModelIndex)
@misc_utils.log_exception(_moduleLogger)
]
title = name
description = name
- self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions)
+ self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions)
@staticmethod
def _choose_phonetype(numberDetails):
class _DraftContact(object):
- def __init__(self, title, description, numbersWithDescriptions):
+ def __init__(self, messageId, title, description, numbersWithDescriptions):
+ self.messageId = messageId
self.title = title
self.description = description
self.numbers = numbersWithDescriptions
message = property(_get_message, _set_message)
- def add_contact(self, contactId, title, description, numbersWithDescriptions):
+ def add_contact(self, contactId, messageId, title, description, numbersWithDescriptions):
if self._busyReason is not None:
raise RuntimeError("Please wait for %r" % self._busyReason)
# Allow overwriting of contacts so that the message can be updated and the SMS dialog popped back up
- contactDetails = _DraftContact(title, description, numbersWithDescriptions)
+ contactDetails = _DraftContact(messageId, title, description, numbersWithDescriptions)
self._contacts[contactId] = contactDetails
self.recipientsChanged.emit()
def get_num_contacts(self):
return len(self._contacts)
+ def get_message_id(self, cid):
+ return self._contacts[cid].messageId
+
def get_title(self, cid):
return self._contacts[cid].title
newMessages = QtCore.pyqtSignal()
historyUpdated = QtCore.pyqtSignal()
dndStateChange = QtCore.pyqtSignal(bool)
+ voicemailAvailable = QtCore.pyqtSignal(str, str)
error = QtCore.pyqtSignal(str)
self._loggedInTime = self._LOGGEDOUT_TIME
self._loginOps = []
self._cachePath = cachePath
+ self._voicemailCachePath = None
self._username = None
self._draft = Draft(self._pool, self._backend, self._errorLog)
self._loggedInTime = self._LOGGEDOUT_TIME
self._backend[0].persist()
self._save_to_cache()
+ self._clear_voicemail_cache()
self.stateChange.emit(self.LOGGEDOUT_STATE)
self.loggedOut.emit()
le = concurrent.AsyncLinearExecution(self._pool, self._set_dnd)
le.start(dnd)
+ def is_available(self, messageId):
+ actualPath = os.path.join(self._voicemailCachePath, "%s.mp3" % messageId)
+ return os.path.exists(actualPath)
+
+ def voicemail_path(self, messageId):
+ actualPath = os.path.join(self._voicemailCachePath, "%s.mp3" % messageId)
+ if not os.path.exists(actualPath):
+ raise RuntimeError("Voicemail not available")
+ return actualPath
+
+ def download_voicemail(self, messageId):
+ le = concurrent.AsyncLinearExecution(self._pool, self._download_voicemail)
+ le.start(messageId)
+
def _set_dnd(self, dnd):
oldDnd = self._dnd
try:
else:
needOps = True
+ self._voicemailCachePath = os.path.join(self._cachePath, "%s.voicemail.cache" % self._username)
+ try:
+ os.makedirs(self._voicemailCachePath)
+ except OSError, e:
+ if e.errno != 17:
+ raise
+
self.loggedIn.emit()
self.stateChange.emit(finalState)
finalState = None # Mark it as already set
self.callbackNumberChanged.emit(self._callback)
self._save_to_cache()
+ self._clear_voicemail_cache()
+
+ def _clear_voicemail_cache(self):
+ import shutil
+ shutil.rmtree(self._voicemailCachePath, True)
def _update_account(self):
try:
if oldDnd != self._dnd:
self.dndStateChange(self._dnd)
+ def _download_voicemail(self, messageId):
+ actualPath = os.path.join(self._voicemailCachePath, "%s.mp3" % messageId)
+ targetPath = "%s.%s.part" % (actualPath, time.time())
+ if os.path.exists(actualPath):
+ self.voicemailAvailable.emit(messageId, actualPath)
+ return
+ with qui_utils.notify_busy(self._errorLog, "Downloading Voicemail"):
+ try:
+ yield (
+ self._backend[0].download,
+ (messageId, targetPath),
+ {},
+ )
+ except Exception, e:
+ self.error.emit(str(e))
+ return
+
+ if os.path.exists(actualPath):
+ try:
+ os.remove(targetPath)
+ except:
+ _moduleLogger.exception("Ignoring file problems with cache")
+ self.voicemailAvailable.emit(messageId, actualPath)
+ return
+ else:
+ os.rename(targetPath, actualPath)
+ self.voicemailAvailable.emit(messageId, actualPath)
+
def _perform_op_while_loggedin(self, op):
if self.state == self.LOGGEDIN_STATE:
op, args, kwds = op