diff options
-rw-r--r-- | diagram.drawio | 2 | ||||
-rw-r--r-- | player_vlc.py | 36 | ||||
-rw-r--r-- | playlist.py | 4 | ||||
-rw-r--r-- | playlist_unittest.py | 53 | ||||
-rw-r--r-- | solo_tool.py | 37 | ||||
-rw-r--r-- | solo_tool_integrationtest.py | 68 |
6 files changed, 166 insertions, 34 deletions
diff --git a/diagram.drawio b/diagram.drawio index d671dd7..3fc9e3f 100644 --- a/diagram.drawio +++ b/diagram.drawio @@ -1 +1 @@ -<mxfile host="app.diagrams.net" modified="2021-12-21T07:22:08.107Z" agent="5.0 (X11)" etag="LSUaRkEzADL58n23WmbH" version="16.0.0" type="device"><diagram id="g-wcGVps3MkI6_XAwNEs" name="Page-1">5VxLd9o6EP41LMmxLb9YJiRN0pPc5lzaJunOYGHU2Ba1BYH++kpYxg8phBJs43qFNXrPzDcjjSR6YBisriNnPrvHLvR7muKueuCyp2mqagP6wyjrhGLpnOBFyOWFMsII/YacqHDqArkwLhQkGPsEzYvECQ5DOCEFmhNF+LVYbIr9Yq9zx4MCYTRxfJH6iFwyS+dlDrKMG4i8Ge/a1qwkI3DSwnwm8cxx8WuOBK56YBhhTJKvYDWEPmNeypek3qc3crcDi2BI9qlw/019Ho0vvl5Z4OXR8MLBr/73vsHHRtbphKFL58+TOCIz7OHQ8a8y6kWEF6ELWasKTWVl7jCeU6JKiT8hIWsuTGdBMCXNSODzXLhC5IlVP7MMnnzmrbHvy1U+sU4TIYnWT2kLLPGca4Kls3qbVFpR5BNnXYwX0YTP2lLtyYv9YzGMwcWXz5/64dAnfa6pxIk8SHYwkZdjnMt1wKVwDXEA6XhogQj6DkHLomY5XEG9bblMhvSDi/EvRGom7S4df8F76mmmT8d/4aIl/fTY53ARbfjBc2hHuUxJ+TmOEUE4TLPGUblwuQmJWt05Y2odCqrg+MgL6feEDgZGlLCEEUEUfuc8I0Cum2gdjNFvZ7xpT9mMCIVkwzvjomdcSgW9S+VZR3DVk9gQ3kkBpgU58lrKmQY0O6nKrVuft7S3pHnjD2wyWcup8VtL6+PpNKb6WNaT7QD3Up3B9Jd+N71VHrWbJ937Go//J2Y6+t2qE0AXOYzRvrOG0Z7iL9qM1xkicDR3Nuh7pY6jqBRvIlaQ2ZuyMewiCw2Lp19zRtzktFnefisfh6WUt0DC2wZMb2pFlTOjYEffMaKZyc5b7NSYyy32XoZXClE19fQ5y7tLYY9neeV41PWiNulAOVOArlNnb9impljFBpP58TY+hFI5d7R9YDpiJkKZ/KWdj3HotcvG7wTbMWy8mvL7UCVKfYNeUpPq7LghUZAmbU1xxXaUBZt03npTZuNDnkEXpPVAXasgMarJpDpgMfHFiSRZ0zGJ8AscYh/Tdi9DHLJWpsj3S6QjuGpdKRlXQ3TVusRTa1V5aquj6DFbiR5xu/PgLGLYGfiAQQk+tiXAx6gTPoOOwsduJXxsQVojQlncFfSUnY+hgWadT7r1+dfRkoY4WgaXdNh5vGw2WiwaMnYmL2zuc0hl0RUEAa2EIFMSadFqhRDoCIS0dkJIDF8IEMrCzV1BUSnkawxMEUWgIhRJo0ytCk/uBSJ5ME3EUAvOeMRA/bnrso5oa2P666MAke6AxyyCZ3t+kgOPXSd2rEawc0CIPoc3qybASQ5VT8tp7Rp1DnAp1CaYMgb7PjsKKwm97kMvqxQKAKDGQy+5nVIEpvyTXmTQSi8iRmquwo2JzxxJgN0OhdJAyY/IDo3VihzJt+B+NLx9/n5+i/77svSfrt2bH01fzznEkRyy+ynh4XiHxm+DsLGVm7h0Y/seH8Xieq3pSxMn4D/0Zv1HBVt5+Tz3vWqmntaySBXXRclGJLlH0BW/YZVwIwmBWXXuP1SzI7AxWgob8YLGcIYxO7XsGHKMEnIGInIGtSLH7ghyLBE5LdiGq+LdjCR2vMT+IujQTqUU8TKBeGxZa8RLa2Shdrj+a/tuFtTT2rJrzewIs4CKouhFG6Vb+jtWapN6gBGiLGCQ+6Dp2l90pyU5ceN3h53NWhnGcZfOulTw/nK5VqevNXxgXFeUkpVqYZgyHbZstdzRA6/Se4TtFaV8oEapCELSp3LNxGl4oDL9zi2aKwpUHnRNQwTdrteGVb9lGSjKGVCz1yugoEkGKGlIMuxjvGWRTlr2UuHojxVP8dnKLhQd5dmKnW4JPqgvW+941Gcr0unv9XL1sHdN7Xy/2h4lUYtmpK+WPU11WiOGoAndaUSCBOs+TbFLsa0qn6DSZPZHAgljs79jAFd/AA==</diagram></mxfile>
\ No newline at end of file +<mxfile host="app.diagrams.net" modified="2021-12-21T17:41:54.343Z" agent="5.0 (X11)" etag="0TS7O8wDBKDC3BJ4ppdc" version="16.0.0" type="device"><diagram id="g-wcGVps3MkI6_XAwNEs" name="Page-1">7Vxbd9o4EP41PJJjW74+BpI22ZNsu0svSd8EFsYbY1FbkKS/fmUs44uEa8AXvOwTlizJ0sx8M6PRiAEYL98+BnC1eMQ28gaKZL8NwM1AUWRVkehPVPPOagzZimucwLVZXVoxcX8hVsk6OmvXRmGuIcHYI+4qXznDvo9mJFcHgwC/5pvNsZf/6go6iKuYzKDH1353bbJgtbJupS/ukOss2KdNxYhfLGHSmK0kXEAbv2aqwO0AjAOMSfy0fBsjL6JeQpe434c9b3cTC5BPqnTQ7kfyF3RzbQZ/Od/wRnG9559DNsoGemu2YDZZ8p5QIMBr30bRINIAjF4XLkGTFZxFb18p02ndgiw9WpLpow3DxbZtVHA8GIas327128ILIrMFK8xdzxtjDwe07GOfjjtik0IBQW97VyvvaEilD+ElIsE7bcI6aBojeyJ4KmPYa8pFDbA2iwwDd5WQSY6zGzslLn1g9BXT+vGr/DyZjr7cGuDlu+b41s/ht6HGkRbZVNZYEQdkgR3sQ+82rR3liZ+2ecB4xaj8DyLknQEHrgnOMwS9ueQp6n6lsdIzGyx6vnnLFt6Tgk+X+5QMEBW2fa4MLSmn/balpOMeJtE143UwY4s2ZHP2Yv5Yj0Mw+vTHh6E/9sgQMGjDwEGkhIasXUS4UikIkAeJu8mDuHaO6gL06B6JcOBu6KMTPY7XwZYe7A39UOaloP0Khy5xsZ+8mgbFxsUhBFL1AKdUE+ckAXqu49PnGZ0MomAbRfhyqaa7Zi+Wrm3HQodC9xecbseTtjNyfbKlnTYaaDdCRpdJvADITF2zj+Q0ogjN0pUCFDMH6ER1VeY0G/xztJh0ZLmgJvL98XweUnksyslugpVEx5r/VB/m99J35e5Jdb6E07+JLlS8nCgske3CiNAefEdBRfYfpK+zynegAFtDpq1GGpoE+AVl3pjKFOh6GcY5Lu/XzWae6JohcbpZ1gW62axBNQu5AX5vBtvQ1YneZcp6p3l/o3bFOj7R/mIdX0lVC0EtJ25YRleXiXh9ulqMYFXNS5MKpCsJqCr1xDRTVyQjP2C8PjbGSbgWU0epAuxJpFSk2YGWIcS+0y+rUAq2OqyCnND7WCFKrIlaEJPmNL8mEJAudU3ex6vFxROuW+1KbZxkGVSOW5+pMeY4RiWZNAesiH1hzEmZM85sz7R3G3WSqValgnJNcJIx1arAUitNWWrjQtGj9xI9/AbpM1yH6GLgA6wCfEyVg4/WJnysC4WP2Uv4mBy3JoSS+FLQUzQ+msLvE1s1PsnW57+OliQo0jO4JNPO4mW70YriJ1M4e4nWvkKUF5eCIKAUEKTx7pustAohcCEQUvoJIT58wUEoDVBfCooKQWLNAjyKRGdJdaBIfGzXsSE6LD4plPAjg488qoQEUloCUdkkMxi6tu3oQ3S0Kf313KVLDkBPEEtUP8FjFYL9OyudAY/ZEHaE3DE6gc4RIfoM3Iwj8HYMvATHsOdltMpmncFbgrQZpoTBnhcdnhWYfv7HZEYheABAi8dkQq1qXajVsXhY7BfErlDAh3Zu/a1NSA3PEtuHxN48NO+v2TEKPpsAO3JDZufr8nEyvn/+dn3v/vlp4z19tO9+nFP2T1Wz08BeqfKJcg+yf2Tez4t2SZ4b8s7d+VubYlJG99amG0ete2sjC7ywM9zk8F5XvMuJsxQuY3OjFSLUggC10WZcwLxUyGgVLYjcrcXgcz/GC4yjA9GLgo1aTADkYWO1GRKQO8ZNa46WwcOkB/t7mU/6iIPSG+ytlxeUTaDnYaMLXLSmQmniMLTE0f4y7I2iVLQ3nUYEklmKzM2xsehexwTM33prstQQfoS3XtRO4MNiAslzxuh0ExMos0BZgJVdHGo6ydySpCsgp2nlIO+/FO+MxdOuI8lcuGhRCnHt947OMZ+8DEW15JObgHH2RHkZKiDfpZZ8cuHyK11CO+7CQT+vovVHSFQ9p0aGctHSNCc1fPSGuMtTj8lqsNFm4dCr1bthQie344SlY6+CKYfeBTvUMd4fkjynOIxwlvyljpLQZWs6rLJuyrNmv9CW6TAL5F2YejRasuPY3apqQJ0J19vx6ZpxVFZHC7vXk0Da1uZVOMlKbo0wr+rcfZU6ca5V9lUSSCr1uC6ggPQmrk+WCW+5XIQoDEu2MvxFe+hDJ71hf7DstHK8e9L/oegFv0o2u/arZL1PjlWN6lcW3ITt9Hy3dJYZoD1guPWSEnBVDhp2fVh12j8JmVpBg7Z3WiXmTCeJEXUCYI/VOtEmGcYeFVd/VK5kWfljKrhB/yNm2Nz5Li2m/6AWMzL9Izpw+y8=</diagram></mxfile>
\ No newline at end of file diff --git a/player_vlc.py b/player_vlc.py new file mode 100644 index 0000000..9c8362a --- /dev/null +++ b/player_vlc.py @@ -0,0 +1,36 @@ +import vlc + +class Player: + def __init__(self): + self.player = vlc.MediaPlayer() + + def play(self): + self.player.play() + + def stop(self): + self.player.stop() + + def pause(self): + self.player.pause() + + def setPlaybackRate(self, rate): + self.player.set_rate(rate) + + def getPlaybackRate(self): + return self.player.get_rate() + + def setPlaybackPosition(self, position): + self.player.set_position(position) + + def getPlaybackPosition(self): + return self.player.get_position() + + def setPlaybackVolume(self, volume): + self.player.audio_set_volume(int(volume * 100)) + + def getPlaybackVolume(self): + return self.player.audio_get_volume() / 100.0 + + def setCurrentSong(self, path): + media = vlc.Media(path) + self.player.set_media(media) diff --git a/playlist.py b/playlist.py index 5c52774..e5a4daf 100644 --- a/playlist.py +++ b/playlist.py @@ -1,10 +1,10 @@ import logging class Playlist: - def __init__(self, callback): + def __init__(self, player): self._songList = list() self._currentSong = None - self._setSongCallback = callback + self._setSongCallback = player.setCurrentSong def addSong(self, path): self._songList.append(path) diff --git a/playlist_unittest.py b/playlist_unittest.py index ff5a79c..06785cc 100644 --- a/playlist_unittest.py +++ b/playlist_unittest.py @@ -1,76 +1,67 @@ from playlist import Playlist +class MockPlayer: + def __init__(self): + self.songSetByCallback = None + + def setCurrentSong(self, path): + self.songSetByCallback = path + def test_addAndSelectOneSong(): songAddedByUser = "/path/to/song" - songSetByCallback = None - - def testCallback(song): - nonlocal songSetByCallback - songSetByCallback = song + mockPlayer = MockPlayer() - uut = Playlist(testCallback) + uut = Playlist(mockPlayer) uut.addSong(songAddedByUser) uut.setCurrentSong(0) - assert songAddedByUser == songSetByCallback + assert songAddedByUser == mockPlayer.songSetByCallback assert uut.getCurrentSong() == songAddedByUser assert uut.getSongs() == [songAddedByUser] def test_addTwoSongsAndSelectBoth(): songAddedByUser = ["/path/to/song", "/path/to/second/song"] - songSetByCallback = None + mockPlayer = MockPlayer() - def testCallback(song): - nonlocal songSetByCallback - songSetByCallback = song - - uut = Playlist(testCallback) + uut = Playlist(mockPlayer) uut.addSong(songAddedByUser[0]) uut.addSong(songAddedByUser[1]) assert uut.getSongs() == songAddedByUser uut.setCurrentSong(0) - assert songAddedByUser[0] == songSetByCallback + assert songAddedByUser[0] == mockPlayer.songSetByCallback assert uut.getCurrentSong() == songAddedByUser[0] uut.setCurrentSong(1) - assert songAddedByUser[1] == songSetByCallback + assert songAddedByUser[1] == mockPlayer.songSetByCallback assert uut.getCurrentSong() == songAddedByUser[1] def test_firstAddedSongIsNotSelected(): songAddedByUser = "/path/to/song" - songSetByCallback = None - - def testCallback(song): - nonlocal songSetByCallback - songSetByCallback = song + mockPlayer = MockPlayer() - uut = Playlist(testCallback) + uut = Playlist(mockPlayer) uut.addSong(songAddedByUser) - assert songSetByCallback == None + assert mockPlayer.songSetByCallback == None assert uut.getCurrentSong() == None assert uut.getSongs() == [songAddedByUser] def test_invalidSongSelection(): songAddedByUser = "/path/to/song" - songSetByCallback = None - - def testCallback(song): - nonlocal songSetByCallback - songSetByCallback = song + mockPlayer = MockPlayer() - uut = Playlist(testCallback) - assert songSetByCallback == None + uut = Playlist(mockPlayer) + assert mockPlayer.songSetByCallback == None assert uut.getCurrentSong() == None uut.setCurrentSong(10) - assert songSetByCallback == None + assert mockPlayer.songSetByCallback == None assert uut.getCurrentSong() == None uut.addSong(songAddedByUser) uut.setCurrentSong(10) - assert songSetByCallback == None + assert mockPlayer.songSetByCallback == None assert uut.getCurrentSong() == None assert uut.getSongs() == [songAddedByUser] diff --git a/solo_tool.py b/solo_tool.py new file mode 100644 index 0000000..da1a402 --- /dev/null +++ b/solo_tool.py @@ -0,0 +1,37 @@ +from playlist import Playlist +from abcontroller import ABController +from session_manager import SessionManager +from player_vlc import Player + +class SoloTool: + def __init__(self, player=None): + self._player = Player() if player is None else player + self._playlist = Playlist(self._player) + self._abController = ABController() + self._sessionManager = SessionManager(self._playlist, self._abController) + + def addSong(self, path): + self._sessionManager.addSong(path) + + def setSong(self, index): + self._playlist.setCurrentSong(index) + + # Playback control + def play(self): + self._player.play() + + def pause(self): + self._player.pause() + + def stop(self): + self._player.stop() + + def setPlaybackRate(self, rate): + self._player.setPlaybackRate(rate) + + def setPlaybackPosition(self, position): + self._player.setPlaybackPosition(position) + + def setPlaybackVolume(self, volume): + self._player.setPlaybackVolume(volume) + diff --git a/solo_tool_integrationtest.py b/solo_tool_integrationtest.py new file mode 100644 index 0000000..19bb406 --- /dev/null +++ b/solo_tool_integrationtest.py @@ -0,0 +1,68 @@ +from solo_tool import SoloTool +from time import sleep + +class MockPlayer(): + STOPPED = 0 + PLAYING = 1 + PAUSED = 2 + + def __init__(self): + self.state = MockPlayer.STOPPED + self.rate = 1.0 + self.position = 0.0 + self.volume = 1.0 + self.currentSong = "" + + def play(self): + self.state = MockPlayer.PLAYING + + def stop(self): + self.state = MockPlayer.STOPPED + + def pause(self): + self.state = MockPlayer.PAUSED + + def setPlaybackRate(self, rate): + self.rate = rate + + def getPlaybackRate(self): + return self.rate + + def setPlaybackPosition(self, position): + self.position = position + + def getPlaybackPosition(self): + return self.position + + def setPlaybackVolume(self, volume): + self.volume = volume + + def getPlaybackVolume(self): + return self.volume + + def setCurrentSong(self, path): + self.currentSong = path + +def test_playerControls(): + mockPlayer = MockPlayer() + uut = SoloTool(mockPlayer) + + assert mockPlayer.state == MockPlayer.STOPPED + uut.play() + assert mockPlayer.state == MockPlayer.PLAYING + uut.pause() + assert mockPlayer.state == MockPlayer.PAUSED + uut.stop() + assert mockPlayer.state == MockPlayer.STOPPED + + assert mockPlayer.rate == 1.0 + uut.setPlaybackRate(0.5) + assert mockPlayer.rate == 0.5 + + assert mockPlayer.position == 0.0 + uut.setPlaybackPosition(0.5) + assert mockPlayer.position == 0.5 + + assert mockPlayer.volume == 1.0 + uut.setPlaybackVolume(0.5) + assert mockPlayer.volume == 0.5 |