Example #1
0
class Builders(object):
    def __init__(self):
        self.conf = Configs()
        if 'RESOURCEPATH' in os.environ:
            self.asset_dir = '{}/assets'.format(os.environ['RESOURCEPATH'])
        else:
            self.dir_name = os.path.dirname(
                os.path.dirname(os.path.abspath(__file__)))
            self.asset_dir = os.path.join(self.dir_name, 'assets')
        pass

    def button_builder(self, parent, label, name):
        button = wx.Button(parent, label=label, name=name)
        button.SetForegroundColour(self.conf.get_attr("TEXT_COLOR"))
        button.SetBackgroundColour(self.conf.get_attr("BACKGROUND_COLOR"))
        return button

    def input_builder(self, parent, name, size=(250, 20), value=""):
        input_field = wx.TextCtrl(parent, size=size, name=name, value=value)
        input_field.SetBackgroundColour(self.conf.get_attr("BACKGROUND_COLOR"))
        input_field.SetForegroundColour(self.conf.get_attr("TEXT_COLOR"))
        return input_field

    def static_text_builder(self, parent, label):
        text = wx.StaticText(parent, label=label)
        text.SetForegroundColour(self.conf.get_attr("TEXT_COLOR"))
        return text

    def build_bitmap_button(self, btn_dict):

        handler = btn_dict['handler']
        name = btn_dict['name']
        parent = btn_dict['parent']
        size_h = btn_dict['size_h']
        size_w = btn_dict['size_w']

        img = wx.Image(os.path.join(self.asset_dir, btn_dict['bitmap']),
                       wx.BITMAP_TYPE_PNG)
        img = img.Scale(size_w, size_h, wx.IMAGE_QUALITY_HIGH)
        img = wx.Bitmap(img)
        btn = buttons.GenBitmapButton(parent=parent, bitmap=img, name=name)
        btn.Bind(wx.EVT_BUTTON, handler)
        return btn

    def build_playlist_cover(self, playlist_dict):
        cover_name = playlist_dict['cover']
        cover_parent = playlist_dict['parent']
        cover = Animation('{}/{}.gif'.format(self.asset_dir, cover_name))
        cover_ctrl = AnimationCtrl(cover_parent, -1, cover, name=cover_name)
        return cover_ctrl
Example #2
0
class Login(object):
    def __init__(self, parent):
        self.builders = Builders()
        self.conf = Configs()
        self.parent = parent
        self.api = YandexAPI()
        self.validation_error = wx.StaticText()
        self.sizer = wx.BoxSizer()
        self.dialog = wx.Dialog()
        self.main_pnl = self.parent.panel
        pass

    def create_login_popup(self):
        self.dialog = dialog = wx.Dialog(self.parent,
                                         wx.ID_ANY,
                                         "",
                                         style=wx.DEFAULT_DIALOG_STYLE
                                         | wx.RESIZE_BORDER,
                                         name="login_popup")
        self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
        dialog.BackgroundColour = self.conf.get_attr("BACKGROUND_COLOR")

        text = self.builders.static_text_builder(dialog, label="Please login")

        font = text.GetFont()
        font.PointSize += 10
        font = font.Bold()
        text.SetFont(font)

        login_label = self.builders.static_text_builder(dialog, label="Login:"******"login_input")

        password_label = self.builders.static_text_builder(dialog,
                                                           label="Password:"******"password_input")

        login_button = self.builders.button_builder(dialog, "Login",
                                                    "login_button")
        login_button.Bind(wx.EVT_BUTTON, self.on_login)

        self.validation_error = self.builders.static_text_builder(dialog,
                                                                  label="")
        self.validation_error.SetForegroundColour(wx.RED)

        sizer.Add(text, 0,
                  wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.TOP, 20)

        sizer.Add(login_label, 0,
                  wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.TOP, 5)

        sizer.Add(login_input, 0,
                  wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.TOP, 10)

        sizer.Add(password_label, 0,
                  wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.TOP, 5)

        sizer.Add(password_input, 0,
                  wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.TOP, 10)

        sizer.Add(login_button, 0,
                  wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.TOP, 10)

        self.sizer.Add(self.validation_error, 1,
                       wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, 5)

        dialog.SetSizer(sizer)
        dialog.Bind(wx.EVT_CLOSE, self.on_popup_close)
        dialog.Center()
        dialog.Show()

    def on_popup_close(self, event):
        if self.api.is_logged_in() is True:
            event.Skip(True)
        else:
            self.parent.on_exit(event)

    def on_login(self, event):
        login_button = self.parent.FindWindowByName("login_button")
        login_button.Disable()
        login = self.parent.FindWindowByName("login_input").GetValue()
        password = self.parent.FindWindowByName("password_input").GetValue()
        try:
            self.api.login(login=login, password=password)
            popup = self.parent.FindWindowByName("login_popup")
            popup.Destroy()
            notify(subtitle="Hello " + self.api.get_display_name())
            self.parent.playlist_selection.Enable(True)
            self.parent.make_menu()
        except BadRequest as e:
            self.validation_error.SetLabel(str(e))
            size = self.dialog.GetSize()
            self.dialog.SetInitialSize()
            self.dialog.SetSize(size)
            login_button.Enable()
            event.Skip()

    def on_logout_menu(self, event):
        self.api.logout()
        self.create_login_popup()
        self.parent.make_menu()
Example #3
0
class Window(wx.Frame):
    def __init__(self, *args, **kw):
        # ensure the parent"s __init__ is called
        super(Window, self).__init__(*args, **kw)

        self.conf = Configs()
        self.api = YandexAPI()
        self.builders = builders.Builders()
        self.panel = main_panel.MainPanel(self)
        self.login = login.Login(self)

        self.SetTitle(self.conf.get_attr("APP_TITLE"))
        self.main_pnl = self.panel.make_main_panel()
        self.input = wx.TextCtrl()
        self.gauge = self.panel.playback_slider
        self.playlists_list = None
        self.account_menu = None
        self.playlist_selection = None
        self.playlists = None

        self.player = Player(parent=self.panel, slider=self.gauge)

        self.Bind(events.FIRST_TRACK_APPEAR, self.on_first_track)
        self.Bind(events.PLAYLIST_READY, self.on_playlist_ready)
        self.gauge.Bind(wx.EVT_SLIDER, self.player.on_seek)

        self.make_menu()
        self.Center()
        self.Show()

        if not self.api.is_logged_in() and wx.FindWindowByName(
                "login_popup") is None:
            self.login.create_login_popup()

    def make_menu(self):

        self.account_menu = wx.Menu()
        if self.api.is_logged_in():
            logout = self.account_menu.Append(1, "&Logout\tCtrl-L",
                                              "Logout from account")
            self.Bind(wx.EVT_MENU, self.login.on_logout_menu, logout)

        player_menu = wx.Menu()

        self.playlists_list = wx.Menu(wx.ID_ANY)
        self.playlist_selection = player_menu.Append(wx.ID_ANY, "Playlists",
                                                     self.playlists_list)
        if self.api.is_logged_in():
            self.playlists = self.api.get_play_lists_list()
            for playlist in self.playlists:
                self.Bind(
                    wx.EVT_MENU, self.on_list_select,
                    self.playlists_list.Append(wx.ID_ANY, playlist['name']))
            pass
        else:
            self.playlist_selection.Enable(False)
            pass

        help_menu = wx.Menu()
        about_item = help_menu.Append(wx.ID_ABOUT)

        menu_bar = wx.MenuBar()
        menu_bar.Append(self.account_menu, "Account")
        menu_bar.Append(player_menu, "Player")
        menu_bar.Append(help_menu, "Help")

        self.SetMenuBar(menu_bar)

        self.Bind(wx.EVT_MENU, self.on_about, about_item)

    def on_first_track(self, event):
        playlist_type = event.playlist_type
        self.gauge.Enable()
        self.panel.enable_play_button()
        self.player.load_playlist(playlist_type)
        pass

    def on_playlist_ready(self, event):
        playlist_name = event.playlist_name
        playlist_type = event.playlist_type
        self.player.load_playlist(playlist_type)
        notify(playlist_name, "Playlist is ready", "")
        pass

    def on_prev(self, event):
        self.player.on_prev(event)
        pass

    def on_next(self, event):
        self.player.on_next(event)
        pass

    def on_play(self, event):
        if not event.GetIsDown():
            self.on_pause(event)
            return
        self.player.on_play(event)
        pass

    def on_pause(self, event):
        self.player.on_pause(event)
        pass

    def on_list_select(self, event):
        playlist_label = event.GetEventObject().GetLabelText(event.GetId())
        playlist_type = None
        for playlist in self.playlists:
            if playlist['name'] == playlist_label:
                playlist_type = playlist['type']
        self.api.preparation(playlist_type, self)

    def on_exit(self, event):
        self.Close()

    def on_about(self, event):
        wx.MessageBox("This is a wxPython Hello World sample",
                      "About Hello World 2", wx.OK | wx.ICON_ASTERISK)
Example #4
0
class YandexAPI(object):

    def __init__(self):
        self.conf = Configs()
        self.client = self.login()
        self.win = None
        self.list_type = None
        self.playlists_list = None
        self.updating_thread = None

        if 'RESOURCEPATH' in os.environ:
            self.cache = '{}/cache'.format(os.environ['RESOURCEPATH'])
        else:
            self.dirName = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            self.cache = os.path.join(self.dirName, 'cache')
        pass

    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            orig = super(YandexAPI, cls)
            cls._instance = orig.__new__(cls)
        return cls._instance

    def login(self, login=None, password=None):
        if self.conf.get_attr("token") is not False:
            client = Client().from_token(self.conf.get_attr("token"))
        elif login is not None and password is not None:
            client = Client().from_credentials(login, password)
            token = client.token
            self.conf.set_attr("token", token)
        else:
            client = Client()
        self.client = client
        return client

    def is_logged_in(self):
        if self.client.account.display_name is None:
            return False
        else:
            return True

    def logout(self):
        self.conf.remove_attr("token")
        self.client = Client()
        pass

    def get_display_name(self):
        return str(self.login().account.display_name)

    def get_play_lists_list(self):
        entities = self.client.landing(blocks="personalplaylists").blocks[0].entities
        lists = []
        for playlist in entities:
            lists.append({
                "name": playlist.data.data.title,
                "type": playlist.data.type
            })
        self.playlists_list = lists
        return lists

    def preparation(self, list_type, win):
        self.updating_thread = threading.Thread(target=self.update)
        self.list_type = list_type
        self.win = win
        index = {
            "date": date.today().__str__(),
            "last_track_num": 1,
            "tracks": []
        }

        if not os.path.exists('{}/{}/'.format(self.cache, list_type)):
            os.mkdir('cache/{}'.format(list_type))

        if not os.path.exists('{}/{}/index.json'.format(self.cache, list_type)):
            with open('{}/{}/index.json'.format(self.cache, list_type), 'w+') as file:
                json.dump(index, file, indent=4)
            self.updating_thread.start()
        else:
            if self.is_need_update():
                with open('{}/{}/index.json'.format(self.cache, list_type), 'w+') as file:
                    json.dump(index, file, indent=4)
                self.updating_thread.start()
            else:
                wx.PostEvent(self.win, events.FirstTrackAppear(playlist_type=list_type))
                playlist_title = ""
                for playlist in self.playlists_list:
                    if playlist['type'] == list_type:
                        playlist_title = playlist['name']
                wx.PostEvent(self.win, events.PlaylistReady(playlist_name=playlist_title, playlist_type=list_type))
                return True

    def is_need_update(self):
        list_type = self.list_type
        with open('{}/{}/index.json'.format(self.cache, list_type), 'r') as file:
            index_date = datetime.strptime(json.load(file)['date'], '%Y-%m-%d').date()
            if index_date == date.today():
                return False
            else:
                return True

    def update(self):
        print("Starting update")
        list_type = self.list_type

        blocks = self.client.landing(blocks="personalplaylists").blocks[0].entities

        playlist = ""

        print("processing blocks")

        for block in blocks:
            if block.data.type == list_type:
                playlist = block.data.data

        tracks = self.client.users_playlists(playlist.kind, playlist.owner.uid)[0].tracks
        index_file = json.load(open('{}/{}/index.json'.format(self.cache, list_type), 'r'))
        index = 1

        print("processing tracks")

        for track in tracks:

            if index == 2:
                wx.PostEvent(self.win, events.FirstTrackAppear(playlist_name=playlist.title, playlist_type=list_type))

            full_track_info = track.track
            index_file['tracks'].append({
                "id": full_track_info.id,
                "title": full_track_info.title,
                "artist": full_track_info.artists[0]['name'],
                "duration": full_track_info.duration_ms,
                "num": index
            })

            with open('{}/{}/index.json'.format(self.cache, list_type), 'w+') as file:
                json.dump(index_file, file)

            track.track.download_cover('{}/{}/{}.png'.format(self.cache, list_type, index))
            track.track.download('{}/{}/{}.mp3'.format(self.cache, list_type, index), codec="mp3", bitrate_in_kbps=320)

            if index == 3:
                break

            index = index + 1
        print("finishing updating")
        wx.PostEvent(self.win, events.PlaylistReady(playlist_name=playlist.title, playlist_type=list_type))
        return True
Example #5
0
class MainPanel(object):
    def __init__(self, parent):

        self.conf = Configs()
        self.parent = parent
        self.api = YandexAPI()
        self.builders = Builders()

        if 'RESOURCEPATH' in os.environ:
            self.asset_dir = '{}/assets'.format(os.environ['RESOURCEPATH'])
        else:
            self.dir_name = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            self.asset_dir = os.path.join(self.dir_name, 'assets')
        pass

        self.main_pnl = wx.Panel(parent)
        self.song_band = None
        self.song_name = None
        self.playback_slider = None
        self.play_pause_btn = None
        self.next_track = None
        self.prev_track = None

    def toggle_gauge_slider(self, slider_type='slider'):
        if slider_type == 'gauge':
            control = wx.Gauge(
                self.main_pnl,
                range=20,
                size=(self.parent.GetSize()[0], 5),
                style=wx.GA_HORIZONTAL | wx.GA_SMOOTH
            ).Pulse()
        else:
            control = wx.Slider(
                self.main_pnl,
                size=(self.parent.GetSize()[0], 5),
                minValue=0,
                maxValue=20
            )
            control.Disable()

        return control

    def make_main_panel(self):

        self.main_pnl.SetBackgroundColour(self.conf.get_attr("BACKGROUND_COLOR"))

        self.playback_slider = self.toggle_gauge_slider()

        main_sizer = wx.BoxSizer(wx.VERTICAL)

        toolbar = self.build_audio_bar()

        main_sizer.Add(
            self.playback_slider,
            0,
            wx.ALL | wx.EXPAND,
            0
        )

        main_sizer.AddStretchSpacer(1)

        main_sizer.Add(
            self.playlist_list(),
            0
        )

        main_sizer.AddStretchSpacer(1)

        main_sizer.Add(
            toolbar,
            0
        )

        self.main_pnl.SetSizer(main_sizer)

        return self.main_pnl

    def build_audio_bar(self):
        """
        Builds the audio bar controls
        """
        audio_bar_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self.prev_track = self.builders.build_bitmap_button({
            'bitmap': 'player_prev.png',
            'handler': self.parent.on_prev,
            'name': 'prev',
            'parent': self.main_pnl,
            'size_h': 30,
            'size_w': 30
        })

        self.song_band = self.builders.static_text_builder(parent=self.main_pnl, label="")
        self.song_name = self.builders.static_text_builder(parent=self.main_pnl, label="")

        song_sizer = wx.BoxSizer(wx.VERTICAL)

        song_sizer.Add(
            self.song_band,
            0
        )

        song_sizer.Add(
            self.song_name,
            0
        )

        audio_bar_sizer.Add(
            self.prev_track,
            0
        )

        img = wx.Image(os.path.join(self.asset_dir, "player_play.png"), wx.BITMAP_TYPE_ANY)
        img = img.Scale(30, 30, wx.IMAGE_QUALITY_HIGH)
        img = wx.Bitmap(img)
        self.play_pause_btn = buttons.GenBitmapToggleButton(self.main_pnl, bitmap=img, name="play")
        self.play_pause_btn.Enable(False)

        img = wx.Image(os.path.join(self.asset_dir, "player_pause.png"), wx.BITMAP_TYPE_ANY)
        img = img.Scale(30, 30, wx.IMAGE_QUALITY_HIGH)
        img = wx.Bitmap(img)
        self.play_pause_btn.SetBitmapSelected(img)
        self.play_pause_btn.SetInitialSize()

        self.play_pause_btn.Bind(wx.EVT_BUTTON, self.parent.on_play)
        audio_bar_sizer.Add(
            self.play_pause_btn,
            0
        )

        self.next_track = self.builders.build_bitmap_button({
            'bitmap': 'player_next.png',
            'handler': self.parent.on_next,
            'name': 'next',
            'parent': self.main_pnl,
            'size_h': 30,
            'size_w': 30
        })

        audio_bar_sizer.Add(
            self.next_track,
            0
        )

        audio_bar_sizer.AddSpacer(5)

        audio_bar_sizer.Add(
            song_sizer,
            0
        )

        self.next_track.Disable()
        self.prev_track.Disable()

        return audio_bar_sizer

    def enable_play_button(self):
        self.play_pause_btn.Enable(True)

    def playlist_list(self):
        playlists_sizer = wx.BoxSizer(wx.HORIZONTAL)
        playlists_sizer.AddSpacer(5)
        playlists = [
            {
                "cover": "playlistOfTheDay",
                "parent": self.main_pnl
            },
            {
                "cover": "neverHeard",
                "parent": self.main_pnl
            },
            {
                "cover": "missedLikes",
                "parent": self.main_pnl
            },
            {
                "cover": "recentTracks",
                "parent": self.main_pnl
            }
        ]
        for playlist in playlists:
            item = self.builders.build_playlist_cover(playlist)
            playlists_sizer.Add(
                item,
                0
            )
            playlists_sizer.AddSpacer(5)
            item.Bind(wx.EVT_ENTER_WINDOW, self.on_hover)
            item.Bind(wx.EVT_LEAVE_WINDOW, self.on_unhover)
            item.Bind(wx.EVT_LEFT_DOWN, self.on_click)
        return playlists_sizer

    def on_click(self, event):
        playlist_type = event.EventObject.GetName()
        self.api.preparation(playlist_type, self.parent)

    def on_unhover(self, event):
        event.EventObject.Stop()

    def on_hover(self, event):
        event.EventObject.Play()