Skip to content
This repository has been archived by the owner on Dec 7, 2020. It is now read-only.

Commit

Permalink
Split library and playlist refresh
Browse files Browse the repository at this point in the history
* split library refresh from playlist refresh
* use some more sane method to schedule refresh
  • Loading branch information
felixb committed Jul 30, 2014
1 parent 9406ef7 commit 008c8b1
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 19 deletions.
1 change: 1 addition & 0 deletions mopidy_gmusic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def get_config_schema(self):
schema['password'] = config.Secret()
schema['deviceid'] = config.String(optional=True)
schema['all_access'] = config.Boolean(optional=True)
schema['refresh_library'] = config.Integer(minimum=-1, optional=True)
schema['refresh_playlists'] = config.Integer(minimum=-1, optional=True)
return schema

Expand Down
75 changes: 56 additions & 19 deletions mopidy_gmusic/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import time

from threading import Timer
from threading import Lock

from mopidy import backend

Expand All @@ -12,6 +12,7 @@
from .library import GMusicLibraryProvider
from .playback import GMusicPlaybackProvider
from .playlists import GMusicPlaylistsProvider
from .repeating_timer import RepeatingTimer
from .session import GMusicSession

logger = logging.getLogger(__name__)
Expand All @@ -21,9 +22,17 @@ class GMusicBackend(pykka.ThreadingActor, backend.Backend):
def __init__(self, config, audio):
super(GMusicBackend, self).__init__()

self._refresh_timer = None
self.config = config

self._refresh_library_rate = \
config['gmusic']['refresh_library'] * 60.0
self._refresh_playlists_rate = \
config['gmusic']['refresh_playlists'] * 60.0
self._refresh_library_timer = None
self._refresh_playlists_timer = None
self._refresh_lock = Lock()
self._refresh_last = 0

self.library = GMusicLibraryProvider(backend=self)
self.playback = GMusicPlaybackProvider(audio=audio, backend=self)
self.playlists = GMusicPlaylistsProvider(backend=self)
Expand All @@ -36,28 +45,56 @@ def on_start(self):
self.config['gmusic']['password'],
self.config['gmusic']['deviceid'])
self.library.set_all_access(self.config['gmusic']['all_access'])

# wait a few seconds to let mopidy settle
# then refresh google music content asynchronously
self._sched_refresh(5.0)
self._refresh_library_timer = RepeatingTimer(
self._refresh_library,
self._refresh_library_rate)
self._refresh_library_timer.start()
# schedule playlist refresh as desired
if self._refresh_playlists_rate > 0:
# skip first playlist refresh
# it's already included in library refresh
self._refresh_playlists_timer = RepeatingTimer(
self._refresh_playlists,
self._refresh_playlists_rate,
self._refresh_playlists_rate)
self._refresh_playlists_timer.start()

def on_stop(self):
if self._refresh_timer:
self._refresh_timer.cancel()
self._refresh_timer = None
if self._refresh_library_timer:
self._refresh_library_timer.cancel()
self._refresh_library_timer = None
if self._refresh_playlists_timer:
self._refresh_playlists_timer.cancel()
self._refresh_playlists_timer = None
self.session.logout()

def _refresh_content(self):
logger.debug('Start refreshing gmusic content')
def _refresh_library(self):
with self._refresh_lock:
t0 = round(time.time())
logger.info('Start refreshing Google Music library')
self.library.refresh()
self.playlists.refresh()
t = round(time.time()) - t0
logger.info('Finished refreshing Google Music content in %ds', t)
self._refresh_last = t0

def _refresh_playlists(self):
if not self._refresh_lock.acquire(False):
# skip, if library is already loading
logger.debug('Skip refresh playlist: library refresh is running.')
return
t0 = round(time.time())
self.library.refresh()
if 0 < self._refresh_library_rate < (t0 - self._refresh_last) - 10.0:
# skip, upcoming library refresh
logger.debug('Skip refresh playlist: ' +
'library refresh is around the corner')
self._refresh_lock.release()
return
logger.info('Start refreshing Google Music playlists')
self.playlists.refresh()
# schedule next refresh
refresh_playlists = self.config['gmusic']['refresh_playlists']
if refresh_playlists > 0:
self._sched_refresh(refresh_playlists * 60.0)
t1 = round(time.time())
logger.debug('Finished refreshing gmusic content, took %ds', t1 - t0)

def _sched_refresh(self, seconds):
self._refresh_timer = Timer(seconds, self._refresh_content)
self._refresh_timer.start()
t = round(time.time()) - t0
logger.info('Finished refreshing Google Music content in %ds', t)
self._refresh_lock.release()
1 change: 1 addition & 0 deletions mopidy_gmusic/ext.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ username =
password =
deviceid =
all_access = false
refresh_library = 1440
refresh_playlists = 60
24 changes: 24 additions & 0 deletions mopidy_gmusic/repeating_timer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from threading import Event, Thread


class RepeatingTimer(Thread):
def __init__(self, method, interval=0, start_after=0):
Thread.__init__(self)
self._stop_event = Event()
self._interval = interval
self._start_after = start_after
self._method = method

def run(self):
if self._start_after > 0 and self._stop_event.wait(self._start_after):
# stopped during start_after
return
# call method the first time
self._method()
while self._interval > 0 and not self._stop_event.wait(self._interval):
# wait for interval
# call method over and over again
self._method()

def cancel(self):
self._stop_event.set()
2 changes: 2 additions & 0 deletions tests/test_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def test_get_config_schema(self):
self.assertIn('username', schema)
self.assertIn('password', schema)
self.assertIn('deviceid', schema)
self.assertIn('refresh_library', schema)
self.assertIn('refresh_playlists', schema)

def test_get_backend_classes(self):
registry = mock.Mock()
Expand Down
43 changes: 43 additions & 0 deletions tests/test_repeating_timer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import logging

import unittest

from threading import Event

from mopidy_gmusic.repeating_timer import RepeatingTimer

logger = logging.getLogger(__name__)


class ExtensionTest(unittest.TestCase):

def setUp(self):
self._event = Event()

def _run_by_timer(self):
self._event.set()
logger.debug('Tick.')

def test_init(self):
timer = RepeatingTimer(self._run_by_timer, 0.5, 0)
timer.start()
self.assertTrue(self._event.wait(1), 'timer was not running')
self._event.clear()
self.assertTrue(self._event.wait(1), 'timer was not running')
timer.cancel()

def test_stop(self):
timer = RepeatingTimer(self._run_by_timer, 10, 0)
timer.start()
self.assertTrue(timer.isAlive(), 'timer is not running')
timer.cancel()
timer.join(1)
self.assertFalse(timer.isAlive(), 'timer is still alive')

def test_start_after(self):
timer = RepeatingTimer(self._run_by_timer, 0.5, 10)
timer.start()
self.assertFalse(self._event.wait(1), 'timer was run! wtf?')
timer.cancel()
timer.join(1)
self.assertFalse(self._event.wait(0), 'timer should not have run')

0 comments on commit 008c8b1

Please sign in to comment.