def test_get_routes(): osc = OSCThreadServer(encoding='utf8') osc.listen(default=True) values = [] @osc.address(u'/test_route') def dummy(*val): pass @osc.address(u'/_oscpy/routes/answer') def cb(*routes): values.extend(routes) send_message(b'/_oscpy/routes', [osc.getaddress()[1]], *osc.getaddress(), encoding='utf8', encoding_errors='strict') timeout = time() + 2 while not values: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9) assert u'/test_route' in values
def test_getaddress(): osc = OSCThreadServer() sock = osc.listen() assert osc.getaddress(sock)[0] == '127.0.0.1' with pytest.raises(RuntimeError): osc.getaddress() sock2 = osc.listen(default=True) assert osc.getaddress(sock2)[0] == '127.0.0.1' osc.stop(sock)
def test_default_handler(): results = [] def test(address, *values): results.append((address, values)) osc = OSCThreadServer(default_handler=test) osc.listen(default=True) @osc.address(b'/passthrough') def passthrough(*values): pass osc.send_bundle(( (b'/test', []), (b'/passthrough', []), (b'/test/2', [1, 2, 3]), ), *osc.getaddress()) timeout = time() + 2 while len(results) < 2: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9) expected = ( (b'/test', tuple()), (b'/test/2', (1, 2, 3)), ) for e, r in zip(expected, results): assert e == r
def test_get_sender(): osc = OSCThreadServer(encoding='utf8') osc.listen(default=True) values = [] @osc.address(u'/test_route') def callback(*val): values.append(osc.get_sender()) with pytest.raises(RuntimeError, match='get_sender\(\) not called from a callback'): osc.get_sender() send_message(b'/test_route', [osc.getaddress()[1]], *osc.getaddress(), encoding='utf8') timeout = time() + 2 while not values: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9)
def test_get_version(): osc = OSCThreadServer(encoding='utf8') osc.listen(default=True) values = [] @osc.address(u'/_oscpy/version/answer') def cb(val): print(val) values.append(val) send_message(b'/_oscpy/version', [osc.getaddress()[1]], *osc.getaddress(), encoding='utf8', encoding_errors='strict') timeout = time() + 2 while not values: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9) assert __version__ in values
def test_bind_default(): osc = OSCThreadServer() osc.listen(default=True) port = osc.getaddress()[1] cont = [] def success(*values): cont.append(True) osc.bind(b'/success', success) send_message(b'/success', [b'test', 1, 1.12345], 'localhost', port) timeout = time() + 5 while not cont: if time() > timeout: raise OSError('timeout while waiting for success message.')
def test_validate_message_address_disabled(): osc = OSCThreadServer(validate_message_address=False) osc.listen(default=True) received = [] @osc.address(b'malformed') def malformed(*val): received.append(val[0]) send_message(b'malformed', [b'message'], *osc.getaddress()) timeout = time() + 2 while not received: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9)
def test_bind_address(): osc = OSCThreadServer() osc.listen(default=True) result = [] @osc.address(b'/test') def success(*args): result.append(True) timeout = time() + 1 send_message(b'/test', [], *osc.getaddress()) while len(result) < 1: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9) assert True in result
def test_encoding_receive(): osc = OSCThreadServer(encoding='utf8') osc.listen(default=True) values = [] @osc.address(u'/encoded') def encoded(*val): for v in val: assert not isinstance(v, bytes) values.append(val) send_message(b'/encoded', [b'hello world', u'ééééé ààààà'.encode('utf8')], *osc.getaddress()) timeout = time() + 2 while not values: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9)
def test_answer(): cont = [] osc_1 = OSCThreadServer() osc_1.listen(default=True) @osc_1.address(b'/ping') def ping(*values): if True in values: osc_1.answer(b'/zap', [True], port=osc_3.getaddress()[1]) else: osc_1.answer(bundle=[(b'/pong', [])]) osc_2 = OSCThreadServer() osc_2.listen(default=True) @osc_2.address(b'/pong') def pong(*values): osc_2.answer(b'/ping', [True]) osc_3 = OSCThreadServer() osc_3.listen(default=True) @osc_3.address(b'/zap') def zap(*values): if True in values: cont.append(True) osc_2.send_message(b'/ping', [], *osc_1.getaddress()) with pytest.raises(RuntimeError) as e_info: # noqa osc_1.answer(b'/bing', []) timeout = time() + 2 while not cont: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9)
def test_bind_address_class(): osc = OSCThreadServer() osc.listen(default=True) @ServerClass class Test(object): def __init__(self): self.result = [] @osc.address_method(b'/test') def success(self, *args): self.result.append(True) timeout = time() + 1 test = Test() send_message(b'/test', [], *osc.getaddress()) while len(test.result) < 1: if time() > timeout: raise OSError('timeout while waiting for success message.') sleep(10e-9) assert True in test.result
"""Count calls.""" global received received += 1 for family in 'unix', 'inet': osc = OSCThreadServer() print(f"family: {family}") if family == 'unix': address, port = '/tmp/test_sock', 0 if os.path.exists(address): os.unlink(address) sock = SOCK = osc.listen(address=address, family='unix') else: SOCK = sock = osc.listen() address, port = osc.getaddress(sock) osc.bind(b'/count', count, sock=sock) for i, pattern in enumerate(patterns): for safer in (False, True): timeout = time() + DURATION sent = 0 received = 0 while time() < timeout: send_message(b'/count', pattern, address, port, sock=SOCK, safer=safer)
def test_advanced_matching(): osc = OSCThreadServer(advanced_matching=True) osc.listen(default=True) port = osc.getaddress()[1] result = {} def save_result(f): name = f.__name__ def wrapped(*args): r = result.get(name, []) r.append(args) result[name] = r return f(*args) return wrapped @osc.address(b'/?') @save_result def singlechar(*values): pass @osc.address(b'/??') @save_result def twochars(*values): pass @osc.address(b'/prefix*') @save_result def prefix(*values): pass @osc.address(b'/*suffix') @save_result def suffix(*values): pass @osc.address(b'/[abcd]') @save_result def somechars(*values): pass @osc.address(b'/{string1,string2}') @save_result def somestrings(*values): pass @osc.address(b'/part1/part2') @save_result def parts(*values): pass @osc.address(b'/part1/*/part3') @save_result def parts_star(*values): pass @osc.address(b'/part1/part2/?') @save_result def parts_prefix(*values): pass @osc.address(b'/part1/[abcd]/part3') @save_result def parts_somechars(*values): pass @osc.address(b'/part1/[c-f]/part3') @save_result def parts_somecharsrange(*values): pass @osc.address(b'/part1/[!abcd]/part3') @save_result def parts_notsomechars(*values): pass @osc.address(b'/part1/[!c-f]/part3') @save_result def parts_notsomecharsrange(*values): pass @osc.address(b'/part1/{string1,string2}/part3') @save_result def parts_somestrings(*values): pass @osc.address(b'/part1/part2/{string1,string2}') @save_result def parts_somestrings2(*values): pass @osc.address(b'/part1/part2/prefix-{string1,string2}') @save_result def parts_somestrings3(*values): pass send_bundle(( (b'/a', [1]), (b'/b', [2]), (b'/z', [3]), (b'/1', [3]), (b'/?', [4]), (b'/ab', [5]), (b'/bb', [6]), (b'/z?', [7]), (b'/??', [8]), (b'/?*', [9]), (b'/prefixab', [10]), (b'/prefixbb', [11]), (b'/prefixz?', [12]), (b'/prefix??', [13]), (b'/prefix?*', [14]), (b'/absuffix', [15]), (b'/bbsuffix', [16]), (b'/z?suffix', [17]), (b'/??suffix', [18]), (b'/?*suffix', [19]), (b'/string1', [20]), (b'/string2', [21]), (b'/string1aa', [22]), (b'/string1b', [23]), (b'/string1?', [24]), (b'/astring1?', [25]), (b'/part1', [26]), (b'/part1/part', [27]), (b'/part1/part2', [28]), (b'/part1/part3/part2', [29]), (b'/part1/part2/part3', [30]), (b'/part1/part?/part2', [31]), (b'/part1', [32]), (b'/part1/a/part', [33]), (b'/part1/b/part2', [34]), (b'/part1/c/part3/part2', [35]), (b'/part1/d/part2/part3', [36]), (b'/part1/e/part?/part2', [37]), (b'/part1/test/part2', [38]), (b'/part1/a/part2', [39]), (b'/part1/b/part2', [40]), (b'/part1/c/part2/part2', [41]), (b'/part1/d/part2/part3', [42]), (b'/part1/0/part2', [43]), (b'/part1/string1/part', [45]), (b'/part1/string2/part3', [46]), (b'/part1/part2/string1', [47]), (b'/part1/part2/string2', [48]), (b'/part1/part2/prefix-string1', [49]), (b'/part1/part2/sprefix-tring2', [50]), ), 'localhost', port) expected = { 'singlechar': [(1, ), (2, ), (3, ), (3, ), (4, )], 'twochars': [(5, ), (6, ), (7, ), (8, ), (9, )], 'prefix': [(10, ), (11, ), (12, ), (13, ), (14, )], 'suffix': [(15, ), (16, ), (17, ), (18, ), (19, )], 'somechars': [(1, ), (2, )], 'somestrings': [(20, ), (21, )], 'parts': [(28, )], 'parts_star': [(30, ), (46, )], 'parts_somestrings': [(46, )], 'parts_somestrings2': [(47, ), (48, )], 'parts_somestrings3': [(49, )] } timeout = time() + 5 while result != expected: if time() > timeout: print("expected: {}\n result: {}\n".format(expected, result)) raise OSError('timeout while waiting for expected result.') sleep(10e-9)
class ServerSong(): def __init__(self, app, pos_callback, state_callback, port): from oscpy.server import OSCThreadServer self.app = app self.pos_callback = pos_callback self.state_callback = state_callback self.state = 'stop' self.length = 30 self.is_complete = False self.song_object = None self.ready = False self.osc = OSCThreadServer() self.osc.listen(port=port, default=True) self.osc.bind(b'/pos', self._get_pos) self.osc.bind(b'/set_state', self.set_state) self.osc.bind(b'/set_length', self.set_length) self.osc.bind(b'/set_complete', self.set_complete) self.osc.bind(b'/playing', self.playing) self.osc.bind(b'/update_playlist', self.update_playlist) self.osc.bind(b'/ready', self.ready_callback) def ready_callback(self): self.ready = True Logger.debug("ACTIVITY: Service is ready.") def playing(self, id, pos): Logger.debug('ACTIVITY: Playing.') self.state = 'play' self.last_pos = pos song = self.app.playlist.get_track(id=id) play_button = self.app.play_button if self.song_object != song: play_button.load_song(song, playing=True) play_button.update_track_current(current=pos) if play_button.event: play_button.event.cancel() play_button.event = Clock.schedule_interval( play_button.update_track_current, 1) def set_state(self, value): Logger.debug('ACTIVITY: State %s', value) self.state = value.decode() def set_length(self, value): Logger.debug('ACTIVITY: Length %s', value) self.length = value def set_complete(self, value): Logger.debug('ACTIVITY: Song is_complete=%s', value) self.is_complete = value def set_volume(self, value): Logger.debug('ACTIVITY -> Service: Set volume %s', value) self.osc.send_message(b'/set_volume', [value], *self.server_address) def getaddress(self): return self.osc.getaddress() def load(self, song): Logger.debug('ACTIVITY -> SERVER: /load') self.osc.send_message(b'/load', [song.id], *self.server_address) def unload(self): Logger.debug('ACTIVITY -> SERVER: /unload') self.osc.send_message(b'/unload', [], *self.server_address) def play(self, seek, volume): Logger.debug('ACTIVITY -> SERVER: /play pos %s vol %s', seek, volume) self.osc.send_message(b'/play', [seek, volume], *self.server_address) def play_new_playlist(self): Logger.debug('ACTIVITY -> SERVER: /play_new_playlist') self.osc.send_message(b'/play_new_playlist', [], *self.server_address) def load_play(self, song, volume): Logger.debug('ACTIVITY -> SERVER: /load_play %s vol %s', song.id, volume) self.osc.send_message(b'/load_play', [song.id, volume], *self.server_address) def stop(self): Logger.debug('ACTIVITY -> SERVER: /stop') self.osc.send_message(b'/stop', [], *self.server_address) def seek(self, position): Logger.debug('ACTIVITY -> SERVER: /seek %s', position) self.osc.send_message(b'/seek', [position], *self.server_address) def save_pos(self, callback): Logger.debug('ACTIVITY -> SERVER: /get_pos') self.pos_callback = callback self.osc.send_message(b'/get_pos', [], *self.server_address) def get_pos(self, callback): Logger.debug('ACTIVITY -> SERVER: get pos and call %s', callback) self.pos_callback = callback self.osc.send_message(b'/get_pos', [], *self.server_address) def _get_pos(self, value): Logger.debug('ACTIVITY: pos received %s', value) self.pos_callback(value) def update_playlist(self): self.app.playlist = self.app.db.get_playlist() Logger.debug('ACTIVITY: Updated playlist.')
class OSCSever: def __init__(self, activity_server_address, port): self.song = SoundAndroidPlayer(self.on_complete) self.osc = OSCThreadServer() self.osc.listen(port=port, default=True) self.activity_server_address = activity_server_address self.osc.bind(b'/get_pos', self.get_pos) self.osc.bind(b'/load', self.load) self.osc.bind(b'/play', self.play) self.osc.bind(b'/load_play', self.load_play) self.osc.bind(b'/seek', self.seek) self.osc.bind(b'/play_new_playlist', self.play_next) self.osc.bind(b'/set_volume', self.set_volume) self.osc.bind(b'/stop', self.pause) self.osc.bind(b'/unload', self.unload) self.api = API() self.db = Database() user = self.db.get_user() self.genres = user['genres'] self.artists = user['artists'] self.volume = user['volume'] self.songs_path = user['songs_path'] self.playlist = self.db.get_playlist() self.waiting_for_load = False self.seek_pos = 0 self.downloading = None self.waiting_for_download = False self.downloads = [] def check_pos(self, *args): if self.song.state == "play" and self.song.length - self.song.get_pos( ) < 20: next_song = self.playlist.preview_next() if next_song is None or next_song.preview_file is None: if next_song is None: self.playlist = self.get_new_playlist() next_song = self.playlist.preview_next() Logger.debug('SERVICE: Preloading next song.') self.download_song(next_song) def thread_download_song(self, song): self.downloads.append(song.id) try: res = self.api.download_preview(song) except Exception as e: Logger.error("SERVICE: Download failed. Reason: %s", e) else: song.preview_file = save_song(self.songs_path, song, res) self.db.update_track(song, 'preview_file', song.preview_file) Logger.debug('SERVICE: Downloading song finished.') self.downloads.remove(song.id) def download_song(self, song): if song.id not in self.downloads and song.preview_file is None: Logger.debug('SERVICE: Downloading %s.', song.id) t = threading.Thread(target=self.thread_download_song, args=(song, )) t.daemon = True t.start() else: Logger.debug( 'SERVICE: Skipped downloading %s. Already in progress.', song.id) def get_new_playlist(self): Logger.debug('SERVICE: getting new playlist.') req = self.api.get_recommendations( self.genres, self.artists, song_type='preview', ) playlist = Playlist(req) self.osc.send_message(b'/update_playlist', [], *self.activity_server_address) # clean up playlist songs favorites = self.db.get_favorites() for song in self.playlist.tracks: if (song.preview_file and song not in favorites and song != self.song.song_object): Logger.debug("Service: Removed %s", song.id) os.remove(song.preview_file) return playlist def get_pos(self, *values): pos = self.song.get_pos() if self.song and self.song.is_prepared else 0 Logger.debug('SERVICE -> ACTIVITY: /pos %s', pos) self.osc.answer(b'/pos', [pos]) def getaddress(self): return self.osc.getaddress() def load(self, id): self.first_load = not getattr(self.song, "id", 0) self.song.id = id self.song.is_prepared = False Logger.debug('SERVICE: Loading %d.', id) song = self.db.get_track(id) if song.preview_file is None and self.downloading != song.id: Logger.debug('SERVICE: %d is not downloaded.', id) self.download_song(song) if song.id in self.downloads: Logger.debug('SERVICE: %d is downloading. Returning.', id) self.waiting_for_download = song.id return Logger.debug('SERVICE: %d file is available.', id) self.waiting_for_download = None if not self.first_load: self.song.reset() self.song.load(song.preview_file) self.song.song_object = song self.db.update_current_track(song) self.playlist = self.db.get_playlist() self.song.is_prepared = True self.update_notification() Logger.debug('SERVICE: Song loaded.') def load_play(self, id, volume=None): Logger.debug('SERVICE: Loading and playing %d.', id) self.pause() self.load(id) self.play(0, volume if volume is not None else self.volume) def play(self, seek, volume): if not self.song.is_prepared: song_id = getattr(self.song, "id", None) Logger.debug('SERVICE: %s is not prepared.', song_id if song_id else "Song") if not self.waiting_for_download: self.load(self.playlist.current_track.id) else: self.waiting_for_load = True self.seek_pos = seek self.volume = volume return else: self.waiting_for_load = False self.song.is_prepared = True self.song.play() self.song.seek(seek) self.song.volume = volume pos = self.song.get_pos() Logger.debug('SERVICE -> ACTIVITY: /playing %s.', pos) values = [self.song.song_object.id, pos] self.osc.send_message(b'/playing', values, *self.activity_server_address) Logger.debug('SERVICE: Playing %d.', self.song.song_object.id) self.update_notification() def stop(self, *values): Logger.debug('SERVICE: stopping %d.', self.song.song_object.id) self.waiting_for_load = False if self.song.is_prepared and self.song.state == 'play': self.song.is_prepared = False self.song.stop() self.update_notification() def pause(self, *values): Logger.debug('SERVICE: pausing Song.') self.waiting_for_load = False if self.song.is_prepared and self.song.state == 'play': self.song.pause() self.update_notification() def seek(self, value): Logger.debug('SERVICE: seeking %s.', value) if self.song.is_prepared: self.song.seek(value) def set_volume(self, value): Logger.debug('SERVICE: setting song volume %s.', value) self.volume = value if self.song.is_prepared: self.song.volume = value def on_complete(self, *values): Logger.debug('SERVICE -> ACTIVITY: /set_complete') self.osc.send_message(b'/set_complete', [True], *self.activity_server_address) self.play_next() def play_next(self): Logger.debug('SERVICE: Playing next.') self.stop() if self.playlist.is_last: self.playlist = self.get_new_playlist() song = self.playlist.next() self.db.update_playlist(self.playlist) self.load_play(song.id) def play_previous(self): Logger.debug('SERVICE: Playing previous.') if not self.playlist.is_first: self.stop() self.load_play(self.playlist.previous().id) def on_state(self, instance, value): Logger.debug('SERVICE -> ACTIVITY: /set_state %s', value) self.osc.send_message(b'/set_state', [value.encode()], *self.activity_server_address) if self.song.is_prepared and self.song.length - self.song.get_pos( ) < 0.2: self.on_complete() def unload(self, *values): self.song.unload() def update_notification(self): notification = self.create_notification() notification_manager = context.getSystemService( Context.NOTIFICATION_SERVICE) notification_manager.notify(1, notification) def create_notification(self): song = getattr(self.song, "song_object", None) if api_version >= 26: builder = NotificationCompatBuilder(context, "gtplayer") else: builder = NotificationCompatBuilder(context) (builder.setContentTitle(song.name if song else "GTPlayer"). setContentText(song.artist if song else "GTPlayer").setContentIntent( controller.getSessionActivity()).setDeleteIntent( MediaButtonReceiver.buildMediaButtonPendingIntent( context, PlaybackStateCompat.ACTION_STOP)).setVisibility( NotificationCompat.VISIBILITY_PUBLIC).setSmallIcon( icon)) style = MediaStyle().setShowActionsInCompactView(0) builder.setStyle(style) if song is not None and not self.playlist.is_first: previous_intent = MediaButtonReceiver.buildMediaButtonPendingIntent( context, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) action = NotificationCompatAction(RDrawable.ic_media_previous, "Previous", previous_intent) builder.addAction(action) if self.song.state == "play": intent = MediaButtonReceiver.buildMediaButtonPendingIntent( context, PlaybackStateCompat.ACTION_PAUSE) action = NotificationCompatAction(RDrawable.ic_media_pause, "Pause", intent) else: intent = MediaButtonReceiver.buildMediaButtonPendingIntent( context, PlaybackStateCompat.ACTION_PLAY) action = NotificationCompatAction(RDrawable.ic_media_play, "Play", intent) builder.addAction(action) coverart = None if song is not None: next_intent = MediaButtonReceiver.buildMediaButtonPendingIntent( context, PlaybackStateCompat.ACTION_SKIP_TO_NEXT) action = NotificationCompatAction(RDrawable.ic_media_next, "Next", next_intent) builder.addAction(action) path = os.path.join(images_path, f"{song.id}.png") if os.path.isfile(path): coverart = path coverart = coverart if coverart is not None else "images/empty_coverart.png" builder.setLargeIcon(BitmapFactory.decodeFile(coverart)) return builder.build()