Skip to content

Commit

Permalink
Rework plugins to have more shared code, standardize some things (#795)
Browse files Browse the repository at this point in the history
  • Loading branch information
aw-was-here committed May 7, 2023
1 parent 75edafa commit 2f65bc8
Show file tree
Hide file tree
Showing 27 changed files with 129 additions and 303 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@

* Experimental feature: Given an option to use Musicbrainz to fill in missing
metadata based only on artist and title (and album if available).
* On Windows, the ability to read from Windows Media Transport compatible
software, such as Amazon Music, Soundcloud, and likely others. (Ironically,
Windows Media Player doesn't appear to use it for whatever reason.)
* Ability to disable reading Virtual DJ remix fields from the M3U history file.
This feature has no impact on what is read from the media itself. In other words,
if the MP3 is tagged with '(Remix)' that will still show up.
* Twitch redemptions using the 'Twofer' format now has the track title as optional.
* The internal twitch lock should now be less likely to deadlock.
* Some log messages have been bumped up from debut to error.
* Unit tests ran during development have been improved.
* Rework the development process.
* Doc updates! Of course.
* Rework the development process; now almost entirely `pyproject.yaml`-based.
* Some major doc changes here and there.
* Rework and simplify some of the internals of plugins.
* Along with that, sources that do not have the required operating
system component installed won't show up as a possible selection in the UI.
* The usual dependency updates that should improve program speed and dependability.

## Version 4.0.3 - 2023-03-26
Expand Down
2 changes: 1 addition & 1 deletion NowPlaying.spec
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def windows_version_file():
'file_description': 'NowPlaying',
'internal_name': 'NowPlaying',
'legal_copyright':
f'{__VERSION__} (c) 2020-2021 Ely Miranda, (c) 2021 Allen Wittenauer',
f'{__VERSION__} (c) 2020-2021 Ely Miranda, (c) 2021-2023 Allen Wittenauer',
'original_filename': 'NowPlaying.exe',
'product_name': 'Now Playing',
'version': '.'.join(getsplitversion()[:3] + ['0'])
Expand Down
3 changes: 2 additions & 1 deletion nowplaying/artistextras/discogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ class Plugin(ArtistExtrasPlugin):
''' handler for discogs '''

def __init__(self, config=None, qsettings=None):
super().__init__(config=config, qsettings=qsettings)
self.displayname = "Discogs"
self.client = None
self.version = config.version
self.there = re.compile('(?i)^the ')
super().__init__(config=config, qsettings=qsettings)

def _find_discogs_releaselist(self, metadata):
try:
Expand Down
3 changes: 2 additions & 1 deletion nowplaying/artistextras/fanarttv.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ class Plugin(ArtistExtrasPlugin):
''' handler for fanart.tv '''

def __init__(self, config=None, qsettings=None):
super().__init__(config=config, qsettings=qsettings)
self.client = None
self.version = config.version
super().__init__(config=config, qsettings=qsettings)
self.displayname = "fanart.tv"

def _fetch(self, apikey, artistid):
artistrequest = None
Expand Down
1 change: 1 addition & 0 deletions nowplaying/artistextras/theaudiodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, config=None, qsettings=None):
super().__init__(config=config, qsettings=qsettings)
self.fnstr = None
self.there = re.compile('(?i)^the ')
self.displayname = "TheAudioDB"

@staticmethod
def _filter(text):
Expand Down
2 changes: 1 addition & 1 deletion nowplaying/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def _defaults_plugins(self, settings):
for key in plugtypelist:
self.pluginobjs[plugintype][key] = self.plugins[plugintype][
key].Plugin(config=self, qsettings=settings)
if self.pluginobjs[plugintype][key].available:
if self.testmode or self.pluginobjs[plugintype][key].available:
self.pluginobjs[plugintype][key].defaults(settings)
else:
removelist.append(key)
Expand Down
58 changes: 11 additions & 47 deletions nowplaying/inputs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,38 @@

import logging
from nowplaying.exceptions import PluginVerifyError
from nowplaying.plugin import WNPBasePlugin


class InputPlugin():
class InputPlugin(WNPBasePlugin):
''' base class of input plugins '''

def __init__(self, config=None, qsettings=None):
self.available = True
self.plugintype = 'input'
self.config = config
self.qwidget = None
self.uihelp = None
super().__init__(config=config, qsettings=qsettings)

if qsettings:
self.defaults(qsettings)
return
#### Additional UI method

if not self.config:
logging.debug('Plugin was not called with config')
def desc_settingsui(self, qwidget): #pylint: disable=no-self-use
''' description of this input '''
qwidget.setText('No description available.')

#### Autoinstallation methods ####

def install(self): # pylint: disable=no-self-use
''' if a fresh install, run this '''
return False

#### Settings UI methods

def defaults(self, qsettings):
''' (re-)set the default configuration values for this plugin '''
raise NotImplementedError

def connect_settingsui(self, qwidget, uihelp):
''' connect any UI elements such as buttons '''
raise NotImplementedError

def load_settingsui(self, qwidget):
''' load values from config and populate page '''
raise NotImplementedError

def verify_settingsui(self, qwidget): #pylint: disable=no-self-use
''' verify the values in the UI prior to saving '''
raise PluginVerifyError('Plugin did not implement verification.')

def save_settingsui(self, qwidget):
''' take the settings page and save it '''
raise NotImplementedError

def desc_settingsui(self, qwidget):
''' provide a description for the plugins page '''
raise NotImplementedError

#### Mix Mode menu item methods

def validmixmodes(self): #pylint: disable=no-self-use
''' tell ui valid mixmodes '''
#return ['newest', 'oldest']
return ['newest']

raise NotImplementedError

def setmixmode(self, mixmode): #pylint: disable=no-self-use
def setmixmode(self, mixmode): #pylint: disable=no-self-use, unused-argument
''' handle user switching the mix mode: TBD '''
#return mixmode

raise NotImplementedError
return 'newest'

def getmixmode(self): #pylint: disable=no-self-use
''' return what the current mixmode is set to '''
Expand All @@ -75,9 +43,7 @@ def getmixmode(self): #pylint: disable=no-self-use
# depending upon other configuration that may be in
# play

#return 'newest'

raise NotImplementedError
return 'newest'

#### Data feed methods

Expand All @@ -94,9 +60,7 @@ async def getrandomtrack(self, playlist):

async def start(self):
''' any initialization before actual polling starts '''
raise NotImplementedError

async def stop(self):
''' stopping either the entire program or just this
input '''
raise NotImplementedError
23 changes: 1 addition & 22 deletions nowplaying/inputs/icecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class Plugin(InputPlugin):
def __init__(self, config=None, qsettings=None):
''' no custom init '''
super().__init__(config=config, qsettings=qsettings)
self.displayname = "Icecast"
self.server = None
self.mode = None
self.lastmetadata = {}
Expand All @@ -166,19 +167,11 @@ def defaults(self, qsettings):
''' (re-)set the default configuration values for this plugin '''
qsettings.setValue('icecast/port', '8000')

def connect_settingsui(self, qwidget, uihelp):
''' connect any UI elements such as buttons '''
self.qwidget = qwidget
self.uihelp = uihelp

def load_settingsui(self, qwidget):
''' load values from config and populate page '''
qwidget.port_lineedit.setText(
self.config.cparser.value('icecast/port'))

def verify_settingsui(self, qwidget): #pylint: disable=no-self-use
''' verify the values in the UI prior to saving '''

def save_settingsui(self, qwidget):
''' take the settings page and save it '''
self.config.cparser.setValue('icecast/port',
Expand All @@ -190,20 +183,6 @@ def desc_settingsui(self, qwidget):
'Icecast is a streaming broadcast protocol.'
' This setting should be used for butt, MIXXX, and many others.')

#### Mix Mode menu item methods

def validmixmodes(self): #pylint: disable=no-self-use
''' tell ui valid mixmodes '''
return ['newest']

def setmixmode(self, mixmode): #pylint: disable=no-self-use
''' handle user switching the mix mode: TBD '''
return 'newest'

def getmixmode(self): #pylint: disable=no-self-use
''' return what the current mixmode is set to '''
return 'newest'

#### Data feed methods

async def getplayingtrack(self):
Expand Down
51 changes: 2 additions & 49 deletions nowplaying/inputs/json.py → nowplaying/inputs/jsonreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,10 @@ class Plugin(InputPlugin):
def __init__(self, config=None, qsettings=None):
''' no custom init '''
super().__init__(config=config, qsettings=qsettings)
self.displayname = "JSONReader"
self.available = False
self.playlists = None

def install(self):
''' auto-install '''
return False

#### Settings UI methods

def defaults(self, qsettings):
''' (re-)set the default configuration values for this plugin '''

def connect_settingsui(self, qwidget, uihelp):
''' connect any UI elements such as buttons '''
self.qwidget = qwidget
self.uihelp = uihelp

def load_settingsui(self, qwidget):
''' load values from config and populate page '''

def verify_settingsui(self, qwidget): #pylint: disable=no-self-use
''' verify the values in the UI prior to saving '''

def save_settingsui(self, qwidget):
''' take the settings page and save it '''

def desc_settingsui(self, qwidget):
''' provide a description for the plugins page '''

#### Mix Mode menu item methods

def validmixmodes(self): #pylint: disable=no-self-use
''' tell ui valid mixmodes '''
return ['newest']

def setmixmode(self, mixmode): #pylint: disable=no-self-use
''' handle user switching the mix mode: TBD '''
return 'newest'

def getmixmode(self): #pylint: disable=no-self-use
''' return what the current mixmode is set to '''
return 'newest'

#### Data feed methods

Expand Down Expand Up @@ -101,13 +64,3 @@ def load_playlists(self, dirpath, playlistfile):
str(dirpath),
) for value in filelist
]


#### Control methods

async def start(self):
''' any initialization before actual polling starts '''

async def stop(self):
''' stopping either the entire program or just this
input '''
16 changes: 1 addition & 15 deletions nowplaying/inputs/m3u.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Plugin(InputPlugin):

def __init__(self, config=None, m3udir=None, qsettings=None):
super().__init__(config=config, qsettings=qsettings)
self.displayname = "M3U"
if m3udir and os.path.exists(m3udir):
self.m3udir = m3udir
else:
Expand Down Expand Up @@ -222,21 +223,6 @@ async def getrandomtrack(self, playlist):
''' not supported '''
return None

def defaults(self, qsettings): #pylint: disable=no-self-use
pass

def validmixmodes(self): #pylint: disable=no-self-use
''' let the UI know which modes are valid '''
return ['newest']

def setmixmode(self, mixmode): #pylint: disable=no-self-use
''' set the mixmode '''
return 'newest'

def getmixmode(self): #pylint: disable=no-self-use
''' get the mixmode '''
return 'newest'

async def stop(self):
''' stop the m3u plugin '''
self._reset_meta()
Expand Down
27 changes: 1 addition & 26 deletions nowplaying/inputs/mpris2.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ class Plugin(InputPlugin):
def __init__(self, config=None, qsettings=None):

super().__init__(config=config, qsettings=qsettings)
self.displayname = "MPRIS2"
self.mpris2 = None
self.service = None

Expand Down Expand Up @@ -225,29 +226,6 @@ async def getrandomtrack(self, playlist):
''' not supported '''
return None

def defaults(self, qsettings):
qsettings.setValue('mpris2/service', None)

def validmixmodes(self):
''' let the UI know which modes are valid '''
return ['newest']

def setmixmode(self, mixmode):
''' only support newest for now '''
return 'newest'

def getmixmode(self):
''' only support newest for now '''
return 'newest'

async def stop(self):
''' not needed '''

def connect_settingsui(self, qwidget, uihelp):
''' not needed '''
self.qwidget = qwidget
self.uihelp = uihelp

def load_settingsui(self, qwidget):
''' populate the combobox '''
if not self.dbus_status or not self.mpris2:
Expand All @@ -260,9 +238,6 @@ def load_settingsui(self, qwidget):
Qt.MatchContains):
curbutton[0].setSelected(True)

def verify_settingsui(self, qwidget):
''' no verification to do '''

def save_settingsui(self, qwidget):
''' save the combobox '''
if not self.dbus_status:
Expand Down
2 changes: 1 addition & 1 deletion nowplaying/inputs/serato.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ class Plugin(InputPlugin): #pylint: disable=too-many-instance-attributes

def __init__(self, config=None, qsettings=None):
super().__init__(config=config, qsettings=qsettings)

self.displayname = "Serato"
self.url = None
self.libpath = None
self.local = True
Expand Down
1 change: 1 addition & 0 deletions nowplaying/inputs/traktor.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class Plugin(IcecastPlugin):
def __init__(self, config=None, qsettings=None):
''' no custom init '''
super().__init__(config=config, qsettings=qsettings)
self.displayname = "Traktor"
self.extradb = None

def install(self):
Expand Down
1 change: 1 addition & 0 deletions nowplaying/inputs/virtualdj.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Plugin(M3UPlugin):

def __init__(self, config=None, m3udir=None, qsettings=None):
super().__init__(config=config, m3udir=m3udir, qsettings=qsettings)
self.displayname = "VirtualDJ"
self.databasefile = pathlib.Path(
QStandardPaths.standardLocations(
QStandardPaths.CacheLocation)[0]).joinpath(
Expand Down
Loading

0 comments on commit 2f65bc8

Please sign in to comment.