mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-15 10:48:33 +01:00
Add a script for Digitally Imported radio (www.di.fm) support. It's not installed yet, and it doesn't support Premium accounts.
This commit is contained in:
parent
32b599f70c
commit
3dce88f94a
BIN
scripts/digitallyimported-radio/icon-small.png
Normal file
BIN
scripts/digitallyimported-radio/icon-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 211 B |
BIN
scripts/digitallyimported-radio/icon.png
Normal file
BIN
scripts/digitallyimported-radio/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
28
scripts/digitallyimported-radio/main.py
Normal file
28
scripts/digitallyimported-radio/main.py
Normal file
@ -0,0 +1,28 @@
|
||||
from service import DigitallyImportedService
|
||||
from settingsdialog import SettingsDialog
|
||||
|
||||
import clementine
|
||||
|
||||
class Plugin:
|
||||
def __init__(self):
|
||||
self.settings_dialog = None
|
||||
|
||||
# Create the service and add it to the Internet tab
|
||||
self.service = DigitallyImportedService(clementine.radio_model)
|
||||
clementine.radio_model.AddService(self.service)
|
||||
|
||||
# Register for when the user clicks the Settings button
|
||||
script.SettingsDialogRequested.connect(self.ShowSettings)
|
||||
|
||||
# Register for the "Configure..." right click menu item
|
||||
self.service.SettingsDialogRequested.connect(self.ShowSettings)
|
||||
|
||||
def ShowSettings(self):
|
||||
# Create the dialog the first time it's shown
|
||||
if not self.settings_dialog:
|
||||
self.settings_dialog = SettingsDialog()
|
||||
|
||||
# Show the dialog
|
||||
self.settings_dialog.show()
|
||||
|
||||
plugin = Plugin()
|
9
scripts/digitallyimported-radio/script.ini
Normal file
9
scripts/digitallyimported-radio/script.ini
Normal file
@ -0,0 +1,9 @@
|
||||
[Script]
|
||||
name=Digitally Imported internet radio
|
||||
description=Digitally Imported is a multi-channel Internet radio service specializing in Electronic Dance Music genres. Installing this plugin will add the list of Digitally Imported radio stations to your Internet tab.
|
||||
author=David Sansome <me@davidsansome.com>
|
||||
url=http://www.di.fm
|
||||
icon=icon.png
|
||||
|
||||
language=python
|
||||
script_file=main.py
|
193
scripts/digitallyimported-radio/service.py
Normal file
193
scripts/digitallyimported-radio/service.py
Normal file
@ -0,0 +1,193 @@
|
||||
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 DigitallyImportedService(clementine.RadioService):
|
||||
SERVICE_NAME = "digitally_imported"
|
||||
HOMEPAGE_URL = QUrl("http://www.di.fm/")
|
||||
STREAM_LIST_URL = QUrl("http://listen.di.fm/")
|
||||
|
||||
# These have to be in the same order as in the settings dialog
|
||||
PLAYLISTS = [
|
||||
{"premium": False, "url": "http://listen.di.fm/public3/%s.pls"},
|
||||
{"premium": True, "url": "http://www.di.fm/listen/%s/premium.pls"},
|
||||
{"premium": False, "url": "http://listen.di.fm/public2/%s.pls"},
|
||||
{"premium": True, "url": "http://www.di.fm/listen/%s/64k.pls"},
|
||||
{"premium": True, "url": "http://www.di.fm/listen/%s/128k.pls"},
|
||||
{"premium": False, "url": "http://listen.di.fm/public5/%s.asx"},
|
||||
{"premium": True, "url": "http://www.di.fm/listen/%s/64k.asx"},
|
||||
{"premium": True, "url": "http://www.di.fm/listen/%s/128k.asx"},
|
||||
]
|
||||
|
||||
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.context_index = None
|
||||
self.last_original_url = None
|
||||
self.menu = None
|
||||
self.root = None
|
||||
self.song_loader = None
|
||||
self.task_id = None
|
||||
|
||||
self.ReloadSettings()
|
||||
|
||||
def ReloadSettings(self):
|
||||
settings = QSettings()
|
||||
settings.beginGroup(self.SERVICE_NAME)
|
||||
|
||||
self.audio_type = int(settings.value("audio_type", 0).toPyObject())
|
||||
|
||||
def CreateRootItem(self):
|
||||
self.root = QStandardItem(QIcon(os.path.join(self.path, "icon-small.png")),
|
||||
"Digitally Imported")
|
||||
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()
|
||||
self.menu.addAction(clementine.IconLoader.Load("media-playback-start"),
|
||||
self.tr("Add to playlist"), self.AddToPlaylist)
|
||||
self.menu.addAction(clementine.IconLoader.Load("media-playback-start"),
|
||||
self.tr("Load"), self.LoadToPlaylist)
|
||||
|
||||
self.menu.addSeparator()
|
||||
|
||||
self.menu.addAction(clementine.IconLoader.Load("download"),
|
||||
self.tr("Open www.di.fm 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 Digitally Imported..."), self.SettingsDialogRequested)
|
||||
|
||||
self.context_index = index
|
||||
self.menu.popup(global_pos)
|
||||
|
||||
def AddToPlaylist(self):
|
||||
print "Add to playlist"
|
||||
|
||||
def LoadToPlaylist(self):
|
||||
print "Load to playlist"
|
||||
|
||||
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("Digitally Imported", 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
|
||||
|
||||
key = original_url.host()
|
||||
playlist_url = self.PLAYLISTS[self.audio_type]["url"] % key
|
||||
|
||||
# Start fetching the playlist
|
||||
self.song_loader = clementine.SongLoader(clementine.library)
|
||||
self.song_loader.LoadFinished.connect(self.LoadPlaylistFinished)
|
||||
self.song_loader.Load(QUrl(playlist_url))
|
||||
|
||||
# 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
|
||||
print result
|
||||
return result
|
||||
|
||||
def LoadPlaylistFinished(self, success):
|
||||
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
|
||||
|
||||
# Failed to get the playlist?
|
||||
if not success:
|
||||
self.StreamError.emit("Error loading playlist '%s'" % self.song_loader.url().toString())
|
||||
return
|
||||
|
||||
result = clementine.PlaylistItem.SpecialLoadResult()
|
||||
result.original_url_ = self.last_original_url
|
||||
if len(self.song_loader.songs()) > 0:
|
||||
# Take the first track in the playlist
|
||||
result.type_ = clementine.PlaylistItem.SpecialLoadResult.TrackAvailable
|
||||
result.media_url_ = QUrl(self.song_loader.songs()[0].filename())
|
||||
|
||||
self.AsyncLoadFinished.emit(result)
|
16
scripts/digitallyimported-radio/settingsdialog.py
Normal file
16
scripts/digitallyimported-radio/settingsdialog.py
Normal file
@ -0,0 +1,16 @@
|
||||
from PyQt4.QtGui import QDialog, QIcon
|
||||
import PyQt4.uic
|
||||
|
||||
import os.path
|
||||
|
||||
class SettingsDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
QDialog.__init__(self, parent)
|
||||
|
||||
self.path = os.path.dirname(__file__)
|
||||
|
||||
# Set up the user interface
|
||||
PyQt4.uic.loadUi(os.path.join(self.path, "settingsdialog.ui"), self)
|
||||
|
||||
# Set the icon
|
||||
self.setWindowIcon(QIcon(os.path.join(self.path, "icon-small.png")))
|
205
scripts/digitallyimported-radio/settingsdialog.ui
Normal file
205
scripts/digitallyimported-radio/settingsdialog.ui
Normal file
@ -0,0 +1,205 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SettingsDialog</class>
|
||||
<widget class="QDialog" name="SettingsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>610</width>
|
||||
<height>331</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Digitally Imported settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Account details (Premium)</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Digitally Imported username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="username"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Digitally Imported password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="password">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>You can <b>listen for free</b> without an account, but Premium members can listen to <b>higher quality</b> streams without advertisements.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string><a href="http://www.di.fm/premium/">Upgrade to Premium now</a></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Audio type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="type">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MP3 96k</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MP3 256k (Premium only)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>AAC 32k</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>AAC 64k (Premium only)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>AAC 128k (Premium only)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Windows Media 40k</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Windows Media 64k (Premium only)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Windows Media 128k (Premium only)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SettingsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SettingsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
Loading…
Reference in New Issue
Block a user