Ejemplo n.º 1
0
def main():
    """ Пример отображения 5 последних альбомов пользователя """

    login, password = '******', 'password'
    vk_session = vk_api.VkApi(login, password)

    try:
        vk_session.auth()
    except vk_api.AuthError as error_msg:
        print(error_msg)
        return

    vkaudio = VkAudio(vk_session)

    albums = vkaudio.get_albums(194957739)

    print('\nLast 5:')
    for album in albums[:5]:
        print(album['title'])

    # Ищем треки последнего альбома
    print('\nSearch for', albums[0]['title'])
    tracks = vkaudio.get(album_id=albums[0]['id'])

    for n, track in enumerate(tracks, 1):
        print('{}. {} {}'.format(n, track['title'], track['url']))
Ejemplo n.º 2
0
def main():
    """ Пример отображения 5 последних альбомов пользователя """

    login, password = '******', 'password'
    vk_session = vk_api.VkApi(login, password)

    try:
        vk_session.auth()
    except vk_api.AuthError as error_msg:
        print(error_msg)
        return

    vkaudio = VkAudio(vk_session)

    albums = []
    offset = 0

    while True:
        temp_albums = vkaudio.get_albums(owner_id='194957739', offset=offset)

        if not temp_albums:
            break

        albums += temp_albums
        offset += len(temp_albums)

    print('\nLast 5:')
    for album in albums[:5]:
        print(album['title'])

    # Ищем треки последнего альбома
    print('\nSearch for', albums[0]['title'])
    tracks = vkaudio.get(album_id=albums[0]['id'])

    for n, track in enumerate(tracks, 1):
        print('{}. {} {}'.format(n, track['title'], track['url']))
Ejemplo n.º 3
0
class VkSongs(object):
    """VK-Songs downloads VK audio files"""
    def __init__(self):
        self.logger = VkSongs.get_logger(level=logging.DEBUG)  # TODO: Configurable log
        self.session = requests.Session()

        self.is_logged_in = False

        self.vk = None
        self.vk_audio = None

    def login(self, login_user, login_pass):
        """Logs in to VK"""
        vk_session = vk_api.VkApi(
            login_user, login_pass,
            auth_handler=self.two_factor_handler,
            captcha_handler=self.captcha_handler,
            app_id=6692066,
        )

        try:
            vk_session.auth()
        except vk_api.AuthError as error_msg:
            self.logger.error('Login failed for {0}. Reason: {1}'.format(login_user, error_msg))
            return

        self.vk = vk_session.get_api()
        self.vk_audio = VkAudio(vk_session)
        self.is_logged_in = True

    @staticmethod
    def two_factor_handler():
        key = input("♫ Enter authentication code: ")
        remember_device = True

        return key, remember_device

    @staticmethod
    def captcha_handler(captcha):
        #  TODO: Implement it using PyInquirer
        key = input("♫ Enter captcha code {0}: ".format(captcha.get_url())).strip()

        return captcha.try_again(key)

    @staticmethod
    def get_color(level):
        print(level)
        if level == 'ERROR':
            return Fore.RED
        elif level == 'WARNING':
            return Fore.YELLOW
        else:
            return ''

    @staticmethod
    def get_logger(level=logging.DEBUG):
        """Returns a logger"""
        logger = logging.getLogger(__name__)

        fh = logging.FileHandler('vk-songs.log', 'w')
        fh.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
        fh.setLevel(level)
        logger.addHandler(fh)

        import sys
        sh = logging.StreamHandler(sys.stdout)
        sh.setFormatter(logging.Formatter(Fore.RED + '%(levelname)s: %(message)s'))
        sh.setLevel(logging.ERROR)
        logger.addHandler(sh)

        return logger

    def download(self, song, save_dir='./', tag=False):
        """Downloads the media file"""
        from pathvalidate import sanitize_filename
        import eyed3
        from eyed3.id3 import ID3_V2_4
        url = song.url
        base_name = '{0} - {1}.mp3'.format(song.artist.strip(), song.title.strip())  # TODO: Configurable file mask
        base_name = sanitize_filename(base_name, replacement_text='_')
        file_path = os.path.join(save_dir, base_name)

        if not os.path.isfile(file_path):
            import time
            with open(file_path, 'wb') as media_file:
                try:
                    content = self.session.get(url).content
                except requests.exceptions.ConnectionError:
                    time.sleep(5)
                    content = self.session.get(url).content

                media_file.write(content)

            file_time = time.time()
            os.utime(file_path, (file_time, file_time))

            if tag:
                audio_file = eyed3.load(file_path)
                eyed3.core.log.disabled = True
                eyed3.id3.log.disabled = True
                if audio_file:
                    if not audio_file.tag:
                        audio_file.initTag(version=ID3_V2_4)
                    audio_file.tag.artist = song.artist
                    audio_file.tag.album_artist = song.artist
                    audio_file.tag.title = song.title
                    if song.album is not None:
                        audio_file.tag.album = song.album
                    audio_file.tag.save(version=ID3_V2_4, encoding='utf_8')

            return True
        else:
            return False

    def search(self, query):
        search = self.vk_audio.search(q=query)  # TODO: Generator
        result = []
        for audio in search:
            audio['url'] = audio['url'].split('?', 1)[0]
            result.append(VkSong(num=audio['id'], artist=audio['artist'], title=audio['title'], url=audio['url']))
        if len(result) == 0:
            self.logger.error('No songs found')
        return result

    def search_user(self, owner_id, query):
        search = self.vk_audio.search_user(owner_id=owner_id, q=query)
        result = []
        for audio in search:
            audio['url'] = audio['url'].split('?', 1)[0]
            result.append(VkSong(num=audio['id'], artist=audio['artist'], title=audio['title'], url=audio['url']))
        if len(result) == 0:
            self.logger.error('No songs found')
        return result

    def get_albums(self, owner_id):
        albums = self.vk_audio.get_albums(owner_id=owner_id)  # TODO: Generator
        result = []
        for album in albums:
            result.append(VkAlbum(title=album['title'], album_id=album['id'], owner_id=album['owner_id']))
        if len(result) == 0:
            self.logger.error('No albums found')
        return result

    def get(self, owner_id=None, album_id=None, album_title=None):
        songs = self.vk_audio.get(owner_id=owner_id, album_id=album_id)  # TODO: Generator
        result = []
        for audio in songs:
            audio['url'] = audio['url'].split('?', 1)[0]
            result.append(VkSong(num=audio['id'], artist=audio['artist'],
                                 title=audio['title'], url=audio['url'], album=album_title))
        if len(result) == 0:
            self.logger.error('No songs found')
        return result

    @staticmethod
    def should_login(dict):
        # TODO: Implement user session caching
        return True

    @staticmethod
    def make_dst_dir(destination='./', artist='/'):
        """Creates the destination directory."""
        destination = destination + artist

        try:
            os.makedirs(destination)
        except OSError as err:
            import errno
            if err.errno == errno.EEXIST and os.path.isdir(destination):
                # Directory already exists
                pass
            else:
                # Target dir exists as a file, or a different error
                raise

        return destination
Ejemplo n.º 4
0
class GetAudioListThread(QThread):
    signal = pyqtSignal("PyQt_PyObject")
    str_signal = pyqtSignal(str)
    image_signal = pyqtSignal("QImage")

    def __init__(self, cookie, window):
        QThread.__init__(self)
        self.login = ""
        self.password = ""
        self.user_link = ""
        self.statusBar = None
        self.save_password = False
        self.authorized = False
        self.cookie = cookie
        self.window = window

    def __del__(self):
        self.wait()

    def _user_auth(self):
        if self.login:
            self.session = VkApi(
                login=self.login,
                password=self.password,
                auth_handler=self.auth_handler,
                captcha_handler=self.captcha_handler,
                config_filename=self.cookie,
            )
            self.statusBar.showMessage("Авторизация.")
            self.session.auth()
        else:
            self.statusBar.showMessage(
                "Логин не указан, использование пароля в качестве токена")
            self.session = VkApi(token=self.password,
                                 captcha_handler=self.captcha_handler)
        self.vk_audio = VkAudio(self.session)
        self.authorized = True

    def _get_audio(self):
        tracks = []
        albums = []
        string = str()
        # Try to get post audio list
        post = self.get_group_and_post_id(self.user_link)
        album = self.get_album_id(self.user_link)
        if isinstance(post, tuple):
            owner_id, post_id = post
            self.statusBar.showMessage("Получение списка аудиозаписей поста.")
            string = "Аудиозаписи поста"
            tracks = self.vk_audio.get_post_audio(owner_id, post_id)
            audios = ",".join(["{owner_id}_{id}".format(**i) for i in tracks])
            tracks = self.session.method(method="audio.getById",
                                         values={"audios": audios})
        elif isinstance(album, tuple):
            owner_id, album_id, *_ = album
            self.statusBar.showMessage(
                "Получение списка аудиозаписей альбома.")
            string = "Аудиозаписи альбома"
            tracks = self._get_tracks(owner_id, album_id)
        else:
            user_id = self.get_user_id(self.user_link)
            # Try to get user or group audio list
            # noinspection PyBroadException
            try:
                owner_id = self.session.method("users.get",
                                               dict(user_ids=user_id))[0]
                self.statusBar.showMessage(
                    "Получение списка аудиозаписей пользователя: {first_name} {last_name}"
                    .format(**owner_id))
                string = "Музыка пользователя: {first_name} {last_name}".format(
                    **owner_id)
            except Exception:
                group_id = self.session.method("groups.getById",
                                               dict(group_id=user_id))[0]
                self.statusBar.showMessage(
                    "Получение списка аудиозаписей сообщества: {name}".format(
                        **group_id))
                string = "Музыка сообщества: {}".format(group_id["name"])
                albums = self._get_albums(-group_id["id"])
                tracks = self._get_tracks(-group_id["id"])
            else:
                albums = self._get_albums(owner_id["id"])
                tracks = self._get_tracks(owner_id["id"])
        for album in albums:
            try:
                album["tracks"] = self.vk_audio.get(
                    owner_id=album["owner_id"],
                    album_id=album["id"],
                    access_hash=album["access_hash"],
                )
            except:
                album["tracks"] = self._get_tracks(owner_id["id"], album["id"])
        return tracks, string, albums

    def _get_tracks(self, owner_id, album_id=None, access_hash=None):
        try:
            tracks = self.vk_audio.get(owner_id, album_id, access_hash)
        except:
            values = {"owner_id": owner_id}
            if album_id:
                values.update({"album_id": album_id})
            res = self.session.method(
                method="audio.get",
                values=values,
            )
            count = res["count"]
            offset = 0
            tracks = []
            while count != 0:
                audios = ",".join(
                    ["{owner_id}_{id}".format(**i) for i in res["items"]])
                tracks.extend(
                    self.session.method(method="audio.getById",
                                        values={"audios": audios}))
                offset += 200 if count >= 200 else count % 200
                count -= 200 if count >= 200 else count % 200
                values.update({"offset": offset})
                res = self.session.method(
                    method="audio.get",
                    values=values,
                )
        return tracks

    def _get_albums(self, owner_id):
        try:
            albums = self.vk_audio.get_albums(owner_id["id"])
        except:
            res = self.session.method(
                method="audio.getPlaylists",
                values={"owner_id": owner_id},
            )
            count = res["count"]
            offset = 0
            albums = []
            while count != 0:
                albums.extend(res["items"])
                offset += 10 if count >= 10 else count % 10
                count -= 10 if count >= 10 else count % 10
                res = self.session.method(
                    method="audio.getPlaylists",
                    values={
                        "owner_id": owner_id,
                        "offset": offset
                    },
                )
        return albums

    def auth_handler(self):
        """
        При двухфакторной аутентификации вызывается эта функция.
        :return: key, remember_device
        """
        self.str_signal.emit("Введите код авторизации:")
        while not self.window.key:
            pass
        return self.window.key, self.save_password

    def captcha_handler(self, captcha):
        url = captcha.get_url()
        file = TemporaryFile()
        res = self.session.http.get(url, stream=True)
        res.raw.decode_content = True
        shutil.copyfileobj(res.raw, file)
        file.seek(0)
        image = QImage()
        image.loadFromData(file.read())
        self.image_signal.emit(image)
        while not self.window.key:
            pass
        return captcha.try_again(self.window.key)

    def run(self):
        try:
            if not self.authorized:
                self._user_auth()
            result = self._get_audio()
            self.signal.emit(result)
        except exceptions.BadPassword:
            self.signal.emit("Неверный логин или пароль.")
        except exceptions.LoginRequired:
            self.signal.emit("Требуется логин.")
        except exceptions.PasswordRequired:
            self.signal.emit("Требуется пароль.")
        except (IndexError, AttributeError):
            self.signal.emit(
                "Невозможно получить список аудиозаписей. Проверьте, открыты ли они у пользователя."
            )
        except exceptions.ApiError as e:
            if "113" in str(e):
                self.signal.emit(
                    "Неверная ссылка на профиль пользователя (неверный ID пользователя)."
                )
            elif "100" in str(e):
                self.signal.emit(
                    "Неверная ссылка на профиль пользователя (сообщества).")
            else:
                self.signal.emit(str(e))
        except Exception as e:
            self.signal.emit(str(type(e)) + str(e))

    @staticmethod
    def get_user_id(link):
        result = findall(r"(https?://m?\.?vk\.com/)?(.*)$", link)[0][1]
        return result if result else None

    @staticmethod
    def get_group_and_post_id(link):
        result = findall(r"wall(.*?)_(.*?)$", link)
        return result[0] if result else None

    @staticmethod
    def get_album_id(link):
        link = link.replace("%2F", "/")
        result = findall(r"playlist/(.*)_(.*)_(.*)\?", link)
        if not result:
            result = findall(r"playlist/(.*)_(.*)_(.*)", link)
        if not result:
            result = findall(r"audio_playlist(.*)_(.*)&access_hash=(.*)", link)
        if not result:
            result = findall(r"audio_playlist(.*)_(.*)/(.*)", link)
        if not result:
            result = findall(r"audio_playlist(.*)_(.*)", link)
        return result[0] if result else None
Ejemplo n.º 5
0
class VkDownloader():
    def __init__(self):
        self._session: vk_api.VkApi = None
        self._audio: VkAudio = None
        self._cache: list = []
        self._albums: list = []

    def login(self,
              username,
              password,
              exception_handler=None,
              bad_password_handler=None,
              success_handler=None,
              captcha_handler=None):
        self._session = vk_api.VkApi(username,
                                     password,
                                     config=MemoryConfig,
                                     captcha_handler=captcha_handler)

        try:
            self._session.auth()
        except vk_api.BadPassword:
            if bad_password_handler:
                bad_password_handler()
            return False
        except Exception:
            if exception_handler:
                exception_handler()
            return False

        self._audio = VkAudio(self._session)
        return True

    def get_tracks(self):
        if self._audio and not self._cache:
            self._cache = self._audio.get()
        return self._cache

    def get_albums(self):
        if self._audio and not self._albums:
            self._albums = self._audio.get_albums()
        return self._albums

    def get_album(self, album_number):
        return self._audio.get(album_id=self._albums[album_number]['id'])

    def refresh(self):
        self._cache = self._audio.get()
        self._albums = self._audio.get_albums()

    def download(self,
                 songs,
                 folder,
                 condition="True",
                 overrite=False,
                 start_func=None,
                 end_func=None,
                 while_func=None):
        class DownloadThread(threading.Thread):
            def run(self):
                if not os.path.exists(folder):
                    while_func(("Creating folder...", ))
                    os.makedirs(folder)

                try:
                    eval(condition)
                except Exception:
                    if (while_func):
                        while_func(("Invalid condition!", ))
                    if (end_func):
                        end_func()
                    return

                while_func(("Starting download...", ))

                for i in songs:
                    name = "%s - %s" % (i['artist'], i['title'])

                    if (eval(condition) or (condition == None)):
                        song = requests.get(i['url'], stream=True)
                        size = int(song.headers['Content-Length'])

                        i = 0
                        if os.path.exists(folder + name +
                                          ".mp3") and not overrite:
                            while_func(("Downloading %s..." % name,
                                        "File already downloaded."))
                            continue
                        file = open(folder + name + ".mp3", "wb")

                        for chunk in song.iter_content(chunk_size=1024):
                            if chunk:
                                i += 1
                                file.write(chunk)
                            if while_func:
                                while_func(("Downloading %s..." % name,
                                            "Downloaded %d" %
                                            (i * 1024 * 100 // size) + r'%'))

                        file.close()
                if end_func:
                    end_func()

        DownloadThread().start()

        if start_func:
            start_func()