import clementine from PyQt4.QtCore import QSettings, QUrl from PyQt4.QtGui import QAction, QDesktopServices, QIcon, QMenu, \ QStandardItem from PyQt4.QtNetwork import QNetworkRequest import PyQt4.QtCore import json import operator import os.path class DigitallyImportedServiceBase(clementine.RadioService): # Set these in subclasses HOMEPAGE_URL = None HOMEPAGE_NAME = None STREAM_LIST_URL = None ICON_FILENAME = None SERVICE_NAME = None SERVICE_DESCRIPTION = None PLAYLISTS = [] SETTINGS_GROUP = "digitally_imported" SettingsDialogRequested = PyQt4.QtCore.pyqtSignal() def __init__(self, model): clementine.RadioService.__init__(self, self.SERVICE_NAME, model) self.network = clementine.NetworkAccessManager(self) self.path = os.path.dirname(__file__) self.audio_type = 0 self.username = "" self.password = "" self.context_index = None self.last_original_url = None self.menu = None self.root = None self.task_id = None self.ReloadSettings() def ReloadSettings(self): settings = QSettings() settings.beginGroup(self.SETTINGS_GROUP) self.audio_type = int(settings.value("audio_type", 0).toPyObject()) self.username = unicode(settings.value("username", "").toPyObject().toUtf8()) self.password = unicode(settings.value("password", "").toPyObject().toUtf8()) def CreateRootItem(self): self.root = QStandardItem(QIcon(os.path.join(self.path, self.ICON_FILENAME)), self.SERVICE_DESCRIPTION) self.root.setData(True, clementine.RadioModel.Role_CanLazyLoad) return self.root def LazyPopulate(self, parent): if parent == self.root: # Download the list of streams the first time the user expands the root self.RefreshStreams() def ShowContextMenu(self, index, global_pos): if not self.menu: self.menu = QMenu() for action in self.GetPlaylistActions(): self.menu.addAction(action) self.menu.addAction(clementine.IconLoader.Load("download"), self.tr("Open " + self.HOMEPAGE_NAME + " in browser"), self.Homepage) self.menu.addAction(clementine.IconLoader.Load("view-refresh"), self.tr("Refresh streams"), self.RefreshStreams) self.menu.addSeparator() self.menu.addAction(clementine.IconLoader.Load("configure"), self.tr("Configure..."), self.SettingsDialogRequested.emit) self.context_index = index self.menu.popup(global_pos) def GetCurrentIndex(self): return self.context_index def Homepage(self): QDesktopServices.openUrl(self.HOMEPAGE_URL) def RefreshStreams(self): if self.task_id is not None: return # Request the list of stations reply = self.network.get(QNetworkRequest(self.STREAM_LIST_URL)) reply.finished.connect(self.RefreshStreamsFinished) # Give the user some indication that we're doing something self.task_id = clementine.task_manager.StartTask(self.tr("Getting streams")) def RefreshStreamsFinished(self): # Get the QNetworkReply that called this slot reply = self.sender() reply.deleteLater() if self.task_id is None: return # Stop the spinner in the status bar clementine.task_manager.SetTaskFinished(self.task_id) self.task_id = None # Read the data and parse the json object inside json_data = reply.readAll().data() streams = json.loads(json_data) # Sort by name streams = sorted(streams, key=operator.itemgetter("name")) # Now we have the list of streams, so clear any existing items in the list # and insert the new ones if self.root.hasChildren(): self.root.removeRows(0, self.root.rowCount()) for stream in streams: item = QStandardItem(QIcon(":last.fm/icon_radio.png"), stream["name"]) item.setData(stream["description"], PyQt4.QtCore.Qt.ToolTipRole) item.setData("digitallyimported://%s" % stream["key"], clementine.RadioModel.Role_Url) item.setData(clementine.RadioModel.PlayBehaviour_SingleItem, clementine.RadioModel.Role_PlayBehaviour) item.setData(stream["name"], clementine.RadioModel.Role_Title) item.setData(self.SERVICE_DESCRIPTION, clementine.RadioModel.Role_Artist) self.root.appendRow(item) def playlistitem_options(self): return clementine.PlaylistItem.Options( clementine.PlaylistItem.SpecialPlayBehaviour | clementine.PlaylistItem.PauseDisabled) def StartLoading(self, original_url): result = clementine.PlaylistItem.SpecialLoadResult() if self.task_id is not None: return result if original_url.scheme() != "digitallyimported": return result if self.PLAYLISTS[self.audio_type]["premium"] and \ (len(self.username) == 0 or len(self.password) == 0): self.StreamError.emit(self.tr("You have selected a Premium-only audio type but do not have any account details entered")) return result key = original_url.host() self.LoadStation(key) # Save the original URL so we can emit it in the finished signal later self.last_original_url = original_url # Tell the user what's happening self.task_id = clementine.task_manager.StartTask(self.tr("Loading stream")) result.type_ = clementine.PlaylistItem.SpecialLoadResult.WillLoadAsynchronously result.original_url_ = original_url return result def LoadStation(self, key): raise NotImplementedError() def LoadPlaylistFinished(self): # Get the QNetworkReply that called this slot reply = self.sender() reply.deleteLater() if self.task_id is None: return # Stop the spinner in the status bar clementine.task_manager.SetTaskFinished(self.task_id) self.task_id = None # Try to parse the playlist parser = clementine.PlaylistParser(clementine.library) songs = parser.LoadFromDevice(reply) # Failed to get the playlist? if len(songs) == 0: self.StreamError.emit("Error loading playlist '%s'" % reply.url().toString()) return result = clementine.PlaylistItem.SpecialLoadResult() result.original_url_ = self.last_original_url # Take the first track in the playlist result.type_ = clementine.PlaylistItem.SpecialLoadResult.TrackAvailable result.media_url_ = QUrl(songs[0].filename()) self.AsyncLoadFinished.emit(result)