Esempio n. 1
0
    async def get_playlist_songs_raw(self, playlist_id: typing.Union[int, str],
                                     page: int = 1, page_size: int = 200) -> dict:
        token = await self._get_token(_API_GET_PLAYLIST_SONGS)
        if token is None:
            raise exceptions.ClientError("get playlist songs: can't get token")

        model = {
            'listid': playlist_id,
            'pagingVO': {
                'page': page,
                'pageSize': page_size,
            },
        }

        try:
            _resp = await self.request('GET', _API_GET_PLAYLIST_DETAIL, params=_sign_payload(token, model))
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            raise exceptions.RequestError('get playlist songs: {}'.format(e))

        try:
            resp = await _resp.json(content_type=None)
            _check(resp['ret'], 'get playlist songs')
        except (aiohttp.ClientResponseError, json.JSONDecodeError, KeyError) as e:
            raise exceptions.ResponseError('get playlist songs: {}'.format(e))

        return resp
Esempio n. 2
0
    async def get_artist_songs_raw(self, artist_id: typing.Union[int, str],
                                   page: int = 1, page_size: int = 50) -> dict:
        token = await self._get_token(_API_SEARCH)
        if token is None:
            raise exceptions.ClientError("get artist songs: can't get token")

        model = {
            'pagingVO': {
                'page': page,
                'pageSize': page_size,
            },
        }
        if artist_id.isdigit():
            model['artistId'] = artist_id
        else:
            model['artistStringId'] = artist_id

        try:
            _resp = await self.request('GET', _API_GET_ARTIST_SONGS, params=_sign_payload(token, model))
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            raise exceptions.RequestError('get artist songs: {}'.format(e))

        try:
            resp = await _resp.json(content_type=None)
            _check(resp['ret'], 'get artist songs')
        except (aiohttp.ClientResponseError, json.JSONDecodeError, KeyError) as e:
            raise exceptions.ResponseError('get artist songs: {}'.format(e))

        return resp
Esempio n. 3
0
    async def search_songs_raw(self, keyword: str, page: int = 1, page_size: int = 50) -> dict:
        token = await self._get_token(_API_SEARCH)
        if token is None:
            raise exceptions.ClientError("search songs: can't get token")

        model = {
            'key': keyword,
            'pagingVO': {
                'page': page,
                'pageSize': page_size,
            },
        }

        try:
            _resp = await self.request('GET', _API_SEARCH, params=_sign_payload(token, model))
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            raise exceptions.RequestError('search songs: {}'.format(e))

        try:
            resp = await _resp.json(content_type=None)
            _check(resp['ret'], 'search songs')
        except (aiohttp.ClientResponseError, json.JSONDecodeError, KeyError) as e:
            raise exceptions.ResponseError('search songs: {}'.format(e))

        return resp
Esempio n. 4
0
    async def get_album_raw(self, album_id: typing.Union[int, str]) -> dict:
        token = await self._get_token(_API_SEARCH)
        if token is None:
            raise exceptions.ClientError("get album: can't get token")

        model = {}
        if album_id.isdigit():
            model['albumId'] = album_id
        else:
            model['albumStringId'] = album_id

        try:
            _resp = await self.request('GET',
                                       _API_GET_ALBUM,
                                       params=_sign_payload(token, model))
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            raise exceptions.RequestError('get album: {}'.format(e))

        try:
            resp = await _resp.json(content_type=None)
            _check(resp['ret'], 'get album')
        except (aiohttp.ClientResponseError, json.JSONDecodeError,
                KeyError) as e:
            raise exceptions.ResponseError('get album: {}'.format(e))

        return resp
Esempio n. 5
0
    async def get_songs_raw(self, *song_ids: typing.Union[int, str]) -> dict:
        token = await self._get_token(_API_GET_SONGS)
        if token is None:
            raise exceptions.ClientError("get songs: can't get token")

        if len(song_ids) > _SONG_REQUEST_LIMIT:
            song_ids = song_ids[:_SONG_REQUEST_LIMIT]

        model = {
            'songIds': song_ids,
        }

        try:
            _resp = await self.request('GET',
                                       _API_GET_SONGS,
                                       params=_sign_payload(token, model))
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            raise exceptions.RequestError('get songs: {}'.format(e))

        try:
            resp = await _resp.json(content_type=None)
            _check(resp['ret'], 'get songs')
        except (aiohttp.ClientResponseError, json.JSONDecodeError,
                KeyError) as e:
            raise exceptions.ResponseError('get songs: {}'.format(e))

        return resp
Esempio n. 6
0
    def init(self):
        self._setup_user_dir()
        self._setup_settings_path()
        self._init_settings_file()

        try:
            self.update(self._settings_from_file())
        except (OSError, json.JSONDecodeError) as e:
            self.update(_DEFAULT_SETTINGS)
            raise exceptions.ClientError("Can't load settings from file: {}".format(e))

        platform_id = self.get('music_platform')
        if get_site(platform_id) is None:
            self['music_platform'] = _DEFAULT_SETTINGS['music_platform']
            raise exceptions.ClientError('Unexpected music platform: "{}"'.format(platform_id))

        self.make_download_dir()
Esempio n. 7
0
 def make_download_dir(self, path: str = None):
     if path is None:
         path = self.get('download_dir')
     download_dir = pathlib.Path(path)
     if not download_dir.is_dir():
         try:
             download_dir.mkdir(parents=True)
         except OSError as e:
             self['download_dir'] = _DEFAULT_SETTINGS['download_dir']
             raise exceptions.ClientError("Can't make download dir: {}".format(e))
Esempio n. 8
0
 def save(self, cfg: dict = None):
     if cfg is None:
         cfg = {
             'music_platform': self['music_platform'],
             'download_dir': self['download_dir'],
         }
     try:
         with self.settings_path.open(mode='w') as settings_file:
             json.dump(cfg, settings_file, indent=4)
     except OSError as e:
         raise exceptions.ClientError("Can't save settings to file: {}".format(e))
Esempio n. 9
0
async def concurrent_download(client: api.API, save_path: str,
                              *songs: api.Song) -> None:
    limit = conf.settings.get('limit')
    if limit is None:
        limit = multiprocessing.cpu_count()
    if limit < 1:
        limit = 1
    if limit > 32:
        limit = 32

    save_path = pathlib.Path(conf.settings['download_dir']).joinpath(
        utils.trim_invalid_file_path_chars(save_path))

    if not save_path.is_dir():
        try:
            save_path.mkdir(parents=True)
        except OSError as e:
            raise exceptions.ClientError(
                "Can't make download dir: {}".format(e))

    sem = asyncio.Semaphore(limit)

    async def worker(song: api.Song):
        async with sem:
            song_info = '{} - {}'.format(song.artist, song.name)
            if not song.playable:
                logging.error(
                    'Download [{}] failed: song unavailable'.format(song_info))
                return

            logging.info('Start download: [{}]'.format(song_info))
            filename = utils.trim_invalid_file_path_chars(song_info)
            mp3_file_path = save_path.joinpath(filename + '.mp3')
            if mp3_file_path.is_file() and not conf.settings.get(
                    'force', False):
                logging.info('Song already downloaded: [{}]'.format(song_info))
                return

            try:
                resp = await client.request('GET', song.url)
                f = await aiofiles.open(mp3_file_path, 'wb')
                await f.write(await resp.read())
                await f.close()
            except (aiohttp.ClientError, asyncio.TimeoutError) as err:
                logging.error('Download [{}] failed: {}'.format(
                    song_info, err))
                if mp3_file_path.is_file():
                    try:
                        mp3_file_path.unlink()
                    except OSError:
                        pass
                return

            logging.info('Download [{}] complete'.format(song_info))

            if conf.settings.get('tag'):
                logging.info('Update music metadata: [{}]'.format(song_info))
                await _write_tag(client, mp3_file_path, song)

            if conf.settings.get('lyric'):
                logging.info('Save lyric: [{}]'.format(song_info))
                lrc_file_path = save_path.joinpath(filename + '.lrc')
                await _save_lyric(lrc_file_path, song.lyric)

    tasks = [asyncio.ensure_future(worker(song)) for song in songs]
    await asyncio.gather(*tasks)