Beispiel #1
0
 def header(cls):
     return MusicObject.header_ui(_("Title"),
                                  _("Album"),
                                  _("Artist"),
                                  _("Length"),
                                  _("Rating"),
                                  weights=cls.ui_weights)
Beispiel #2
0
 def restore_history(self):
     try:
         with open(HISTORY_FILE, "r") as f:
             self.history = deserialize(f.read())
     except (AttributeError, FileNotFoundError) as e:
         logging.exception(e)
         print(_("failed to restore recently played. :("))
Beispiel #3
0
    def get_text(self):
        if self.app.current_song is None:
            return _("Idle")

        progress, total = self.get_prog_tot()
        song = self.app.current_song
        rating = ""

        if isinstance(song, Song):
            artist = song.artist

            if song.rating in (1, 5):
                rating = "(" + RATE_UI[song.rating] + ")"

        else:  # YTVideo
            artist = song.channel

        return " {} {} - {} {}[{:d}:{:02d} / {:d}:{:02d}] {}".format(
            ["■", "▶"][self.app.play_state == "play"],
            artist,
            self.app.current_song.title,
            rating,
            *sec_to_min_sec(progress),
            *sec_to_min_sec(total),
            self.vol_inds[self.app.volume],
        )
Beispiel #4
0
    def restore_queue(self):
        try:
            with open(QUEUE_FILE, "r") as f:
                self.queue_panel.add_songs_to_queue(deserialize(f.read()))

        except (AttributeError, FileNotFoundError) as e:
            logging.exception(e)
            print(_("failed to restore queue. :("))
            self.queue_panel.clear()
Beispiel #5
0
    def save_queue(self):
        print(_("saving queue"))
        queue = []

        if self.current_song is not None:
            queue.append(self.current_song)

        queue.extend(self.queue_panel.queue)

        with open(QUEUE_FILE, "w") as f:
            f.write(serialize(queue))
Beispiel #6
0
    def update_search_results(self, *categories, title=None, isprevsong=False):
        if title is None:
            title = _("Search Results")

        if not self.viewing_previous_songs:  # only remember search history
            self.search_history.append(
                (self.get_focus()[1], self.search_results))

        self.viewing_previous_songs = isprevsong

        self.set_search_results(categories)
        self.line_box.set_title(title)
Beispiel #7
0
    def back(self):
        if self.search_history:
            prev_focus, search_history = self.search_history.pop()

            self.set_search_results(list(search_history))
            self.viewing_previous_songs = False
            self.line_box.set_title(_("Search Results"))

            try:
                self.set_focus(prev_focus)
            except:
                pass
Beispiel #8
0
    def login(self):
        self.g_api = gmusicapi.Mobileclient(debug_logging=False)
        self.load_config()

        if not isfile(CRED_FILE):
            from oauth2client.client import FlowExchangeError

            print(_("No local credentials file found."))
            print(
                _("TUIJam will now open a browser window so you can provide"))
            print(
                _("permission for TUIJam to access your Google Play Music account."
                  ))
            input(_("Press enter to continue."))
            try:
                self.g_api.perform_oauth(CRED_FILE, open_browser=True)
            except FlowExchangeError:
                raise RuntimeError(_("Oauth authentication Failed."))

        self.g_api.oauth_login(self.g_api.FROM_MAC_ADDRESS,
                               CRED_FILE,
                               locale=locale.getdefaultlocale()[0])

        if self.lastfm_sk is not None:
            try:
                self.lastfm = LastFMAPI(self.lastfm_sk)
            except Exception:
                print(_("Could not retrieve Last.fm keys."))
                print(_("Scrobbling will not be available."))
            # TODO handle if sk is invalid

        from apiclient.discovery import build

        try:
            developer_key, = lookup_keys("GOOGLE_DEVELOPER_KEY")
            self.youtube = build("youtube", "v3", developerKey=developer_key)
        except Exception:
            self.youtube = None
            print(_("Could not retrieve YouTube key."))
            print(_("YouTube will not be available."))
Beispiel #9
0
    def configure():
        from os.path import join, isfile
        from getpass import getpass

        config_file = join(CONFIG_DIR, "config.yaml")
        if not isfile(config_file):
            print(_("It seems that you haven't run tuijam yet."))
            print(_("Please run it first, then authorize to Last.fm."))
            return

        print(_("generating Last.fm authentication token"))
        api = LastFMAPI()
        token = api.get_token()
        auth_url = api.get_auth_url(token)

        import webbrowser

        webbrowser.open_new_tab(auth_url)

        print()
        print(
            _("Please open this link in your browser and authorize the app in case the window "
              ).join(_("hasn't been opened automatically:")))
        print(auth_url)
        print()
        input(_("After that, press Enter to get your session key..."))
        if not api.auth_by_token(token):
            print(_("Failed to get a session key. Have you authorized?"))
        else:
            with open(config_file, "r+") as f:
                lastfm_sk = api.sk
                config = yaml.safe_load(f.read())
                config.update({"lastfm_sk": lastfm_sk})
                f.seek(0)
                yaml.safe_dump(config, f, default_flow_style=False)
                f.truncate()
                f.close()
            print(_("Successfully authenticated."))
Beispiel #10
0
    def listen_now(self):
        situations = self.g_api.get_listen_now_situations()
        items = self.g_api.get_listen_now_items()
        playlists = self.g_api.get_all_user_playlist_contents()
        liked = self.g_api.get_top_songs()

        situations = [Situation.from_dict(hit) for hit in situations]
        albums = [
            Album.from_dict(hit["album"]) for hit in items if "album" in hit
        ]
        radio_stations = [
            RadioStation.from_dict(hit["radio_station"]) for hit in items
            if "radio_station" in hit
        ]
        playlists = [Playlist.from_dict(playlist) for playlist in playlists]

        liked = [Song.from_dict(song) for song in liked]
        playlists.append(Playlist(_("Liked"), liked, None))

        self.search_panel.update_search_results([], albums, [], situations,
                                                radio_stations, playlists, [])
        self.set_focus(self.search_panel_wrapped)
Beispiel #11
0
def main():
    import argparse

    load_locale()

    parser = argparse.ArgumentParser(
        "TUIJam", description=_("A fancy TUI client for Google Play Music."))
    parser.add_argument("action",
                        choices=["", "configure_last_fm"],
                        default="",
                        nargs="?")
    parser.add_argument("-v", "--verbose",
                        action="store_true")  # TODO: use this
    args = parser.parse_args()

    print(_("starting up."))
    makedirs(CONFIG_DIR, exist_ok=True)

    log_file = join(CONFIG_DIR, "log.txt")
    logging.basicConfig(filename=log_file, filemode="w", level=logging.WARNING)

    if args.action == "configure_last_fm":
        LastFMAPI.configure()
        exit(0)
    elif args.action != "":
        print(f"Unrecognized option: {args.action}")
        exit(0)

    app = App()
    print(_("logging in."))
    app.login()

    if app.mpris_enabled:
        from .mpris import setup_mpris

        print(_("enabling external control."))
        app.mpris = setup_mpris(app)
        if not app.mpris:
            print(_("Failed."))

    if app.persist_queue:
        print(_("restoring queue"))
        app.restore_queue()

    print(_("restoring history"))
    app.restore_history()

    if app.video:
        app.player["vid"] = "auto"

    import signal

    signal.signal(signal.SIGINT, app.cleanup)

    loop = urwid.MainLoop(app, event_loop=urwid.GLibEventLoop())
    loop.screen.set_terminal_properties(256)
    loop.screen.register_palette([(k.replace("-", " "), "", "", "", fg, bg)
                                  for k, (fg, bg) in palette.items()])
    app.loop = loop

    try:
        loop.run()
    except Exception as e:
        logging.exception(e)
        print(
            _("Something bad happened! :( see log file ($HOME/.config/tuijam/log.txt) for more information."
              ))
        app.cleanup()
Beispiel #12
0
 def fmt_str(self):
     return [("np_song", f"{self.title} "),
             _("by "), ("np_artist", f"{self.artist}")]
Beispiel #13
0
 def __str__(self):
     return "{} {}{}".format(self.title, _("by "), self.artist)
Beispiel #14
0
 def header(cls):
     return MusicObject.header_ui(_("Playlist Name"),
                                  _("# Songs"),
                                  weights=cls.ui_weights)
Beispiel #15
0
 def header():
     return MusicObject.header_ui(_("Station Name"))
Beispiel #16
0
 def header():
     return MusicObject.header_ui(_("Situation"), _("Description"))
Beispiel #17
0
 def header():
     return MusicObject.header_ui(_("Artist"))
Beispiel #18
0
 def header():
     return MusicObject.header_ui(_("Album"), _("Artist"), _("Year"))
Beispiel #19
0
 def header(cls):
     return MusicObject.header_ui(_("Youtube"),
                                  _("Channel"),
                                  weights=cls.ui_weights)
Beispiel #20
0
 def __str__(self):
     return "{} {}{}".format(self.title, _("by "), self.channel)
Beispiel #21
0
 def view_previous_songs(self, songs, yt_vids):
     self.update_search_results(songs,
                                yt_vids,
                                title=_("Previous Songs"),
                                isprevsong=True)
Beispiel #22
0
 def __init__(self, app):
     self.app = app
     super().__init__(_("search > "), multiline=False, allow_tab=False)
Beispiel #23
0
    def __init__(self):
        import mpv

        self.player = mpv.MPV()
        self.player.volume = 100
        self.player["vid"] = "no"
        self.volume = 8
        self.g_api = None
        self.loop = None
        self.config_pw = None
        self.reached_end_of_track = False
        self.lastfm = None
        self.youtube = None
        self.mpris = None
        self.vim_mode = None
        self.vim_insert = False

        @self.player.event_callback("end_file")
        def end_file_callback(event):

            if event["event"]["reason"] == 0:

                self.reached_end_of_track = True
                if self.lastfm:
                    self.current_song.lastfm_scrobbled = False
                self.schedule_refresh(dt=0.01)

        self.search_panel = SearchPanel(self)
        search_panel_wrapped = urwid.LineBox(self.search_panel,
                                             title=_("Search Results"))

        # Give search panel reference to LineBox to change the title dynamically
        self.search_panel.line_box = search_panel_wrapped

        search_panel_wrapped = urwid.AttrMap(search_panel_wrapped,
                                             "region_bg normal",
                                             "region_bg select")
        self.search_panel_wrapped = search_panel_wrapped

        self.playbar = PlayBar(self,
                               "progress_remaining",
                               "progress",
                               current=0,
                               done=100)

        self.queue_panel = QueuePanel(self)
        queue_panel_wrapped = urwid.LineBox(self.queue_panel, title=_("Queue"))

        queue_panel_wrapped = urwid.AttrMap(queue_panel_wrapped,
                                            "region_bg normal",
                                            "region_bg select")
        self.queue_panel_wrapped = queue_panel_wrapped

        self.search_input = urwid.Edit("> ", multiline=False)
        self.search_input = SearchInput(self)

        urwid.Pile.__init__(
            self,
            [
                ("weight", 12, search_panel_wrapped),
                ("pack", self.playbar),
                ("weight", 7, queue_panel_wrapped),
                ("pack", self.search_input),
            ],
        )

        self.set_focus(self.search_input)

        self.play_state = "stop"
        self.current_song = None
        self.history = []