├── .gitignore
├── LICENSE
├── README.md
├── music.jpg
└── vktotg.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | local_settings.py
56 |
57 | # Flask stuff:
58 | instance/
59 | .webassets-cache
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # Jupyter Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # SageMath parsed files
80 | *.sage.py
81 |
82 | # dotenv
83 | .env
84 |
85 | # virtualenv
86 | .venv
87 | venv/
88 | ENV/
89 |
90 | # Spyder project settings
91 | .spyderproject
92 | .spyproject
93 |
94 | # Rope project settings
95 | .ropeproject
96 |
97 | # mkdocs documentation
98 | /site
99 |
100 | # mypy
101 | .mypy_cache/
102 |
103 | # cache
104 | MusicSaver.session
105 | vk_config.v2.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 HaCk3D
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vktotg
2 | Sends all your vk.com music to telegram channel
3 |
4 | # Screenshot
5 |
6 |
7 | # Requirements
8 |
9 | `sudo pip install bs4 vk_api telethon requests`
10 |
11 | # Run
12 |
13 | `python vktotg.py`
14 |
15 | Downloads all your music locally to folder `Music `
16 |
17 | You can provide specific `user_id` as argument when launching from command line to dowload audio of this user. Just be sure that you have access to them
18 |
19 | Examle:
20 | `python vktotg.py 28452705`
21 |
--------------------------------------------------------------------------------
/music.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HaCk3Dq/vktotg/f989247bd1947966a603f8c52113de4d015b8938/music.jpg
--------------------------------------------------------------------------------
/vktotg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import os
4 | import sys
5 | import ssl
6 | import shutil
7 | import requests
8 | import webbrowser
9 | import vk_api
10 | from getpass import getpass
11 | from vk_api.audio import VkAudio
12 | from telethon.sync import TelegramClient
13 | from telethon.errors import SessionPasswordNeededError
14 | from telethon.tl.functions.channels import CreateChannelRequest, EditPhotoRequest
15 | from telethon.tl.types import (
16 | DocumentAttributeAudio, DocumentAttributeFilename, Channel, InputChannel, InputChatUploadedPhoto
17 | )
18 |
19 | folderName = 'Music '
20 | channelName = 'VKMusic'
21 |
22 |
23 | def captcha_handler(captcha):
24 | url = captcha.get_url()
25 | key = input("Enter captcha code {0}: ".format(url)).strip()
26 | webbrowser.open(url, new=2, autoraise=True)
27 | return captcha.try_again(key)
28 |
29 |
30 | def auth_handler():
31 | key = input("Enter authentication code: ")
32 | remember_device = True
33 | return key, remember_device
34 |
35 |
36 | def reporthook(sent_bytes, total):
37 | sys.stdout.write(
38 | f'\r{round(sent_bytes / total * 100, 1)}% {round(sent_bytes / 1024 / 1024, 1)}/{round(total / 1024 / 1024, 1)} MB'
39 | )
40 | sys.stdout.flush()
41 |
42 |
43 | def save(url, filename):
44 | response = requests.get(url, stream=True)
45 | with open(filename, 'wb') as out_file:
46 | shutil.copyfileobj(response.raw, out_file)
47 | del response
48 |
49 |
50 | def send_file(client, entity, file, dur, title, artist, caption):
51 | attr_dict = {
52 | DocumentAttributeFilename:
53 | DocumentAttributeFilename(caption),
54 | DocumentAttributeAudio:
55 | DocumentAttributeAudio(int(dur), title=title, performer=artist)
56 | }
57 | client.send_file(
58 | entity, file,
59 | progress_callback=reporthook,
60 | attributes=list(attr_dict.values())
61 | )
62 |
63 |
64 | def auth_vk():
65 | print('First, log in to vk.com')
66 |
67 | vk_session = vk_api.VkApi(
68 | input('Enter login: '),
69 | getpass('Enter password: '),
70 | captcha_handler=captcha_handler,
71 | auth_handler=auth_handler
72 | )
73 |
74 | try:
75 | vk_session.auth()
76 | except vk_api.AuthError as error_msg:
77 | print(error_msg)
78 | exit()
79 |
80 | user_id = vk_session.get_api().users.get()[0]['id']
81 | try:
82 | user_id = str(sys.argv[1])
83 | print(f'Downloading audios from {user_id}')
84 | except:
85 | pass
86 | if not os.path.exists(folderName + str(user_id)):
87 | os.mkdir(folderName + str(user_id))
88 | return VkAudio(vk_session), user_id
89 |
90 |
91 | def auth_tg():
92 | print('\nNow, log in to telegram')
93 | client = TelegramClient('MusicSaver', 184825, '7fd2ade01360bdd6cbc1de0f0120092c').start()
94 | client.connect()
95 | if not client.is_user_authorized():
96 | try:
97 | client.sign_in(phone=input('Enter full phone number: '))
98 | client.sign_in(code=input('Enter code that you received: '))
99 | except SessionPasswordNeededError:
100 | client.sign_in(password=input('Two step verification is enabled. Please enter your password: '))
101 | return client
102 |
103 | def get_last_readable_track_in_channel(telegramMessagesList, vkAudioList):
104 | offset = 0
105 | local_progress = 0
106 | iterator = len(telegramMessagesList)
107 | while iterator > 0:
108 | last_file = telegramMessagesList[offset].document
109 | if last_file:
110 | lastAudioName = last_file.attributes[1].file_name
111 | else:
112 | offset = offset + 1
113 | iterator = iterator - 1
114 | continue
115 | try:
116 | local_progress = vkAudioList.index(lastAudioName)
117 | except ValueError:
118 | print("Vk audio list does not contain value: " + lastAudioName)
119 | if local_progress > 0:
120 | return local_progress+offset
121 | else:
122 | offset = offset + 1
123 | iterator = iterator - 1
124 | return 0
125 |
126 | def main():
127 | store_local = input('Do you want to leave the local files? [N/y] ') in ['y', 'yes']
128 |
129 | vkaudio, user_id = auth_vk()
130 | with auth_tg() as client:
131 |
132 | VKMusicChannel = None
133 | last_file = None
134 | progress = 0
135 |
136 | dialogs = client.get_dialogs(limit=None)
137 | for chat in dialogs:
138 | if type(chat.entity) == Channel and chat.title == channelName:
139 | VKMusicChannel = chat
140 |
141 | if VKMusicChannel is None:
142 | VKMusicChannel = client(CreateChannelRequest(
143 | title=channelName, about='made with https://github.com/HaCk3Dq/vktotg')).chats[0]
144 | client(EditPhotoRequest(
145 | InputChannel(VKMusicChannel.id, VKMusicChannel.access_hash), InputChatUploadedPhoto(
146 | client.upload_file('music.jpg'))
147 | ))
148 | client.delete_messages(client.get_entity(VKMusicChannel), 2)
149 | else:
150 | last_file = client.get_messages(VKMusicChannel, limit=None)[0].document
151 | if last_file:
152 | last_file = last_file.attributes[1].file_name
153 |
154 | audios = vkaudio.get(user_id)
155 | total = len(audios)
156 | if last_file:
157 | vkAudioList = [track['artist'] + ' - ' + track['title'] for track in audios[::-1]]
158 | # try get offset by last saved track
159 | try:
160 | progress = vkAudioList.index(last_file)
161 | except ValueError:
162 | print("Vk audio list does not contain value: " + last_file)
163 | if progress:
164 | progress = progress + 1
165 | else:
166 | # try get offset from last readable track
167 | telegramMessagesList = client.get_messages(VKMusicChannel, limit=None)
168 | progress = get_last_readable_track_in_channel(telegramMessagesList, vkAudioList)+1
169 | if progress == total:
170 | print(f'[Done] Found {progress}/{total} tracks')
171 | exit()
172 | else:
173 | print(f'\nFound {progress}/{total} tracks, continue downloading...')
174 | print()
175 |
176 | progress += 1
177 | for i, track in enumerate(audios[::-1]):
178 | if progress and i < progress - 1:
179 | continue
180 | filename = track['artist'] + ' - ' + track['title']
181 | escaped_filename = filename.replace("/", "_")
182 | escaped_filename = ''.join(e for e in escaped_filename if e.isalnum() or e == '_' or e == ' ')
183 | file_path = folderName + str(user_id) + '/' + escaped_filename + '.mp3'
184 |
185 | print(f'Downloading [{i + 1}/{total}]')
186 | print(filename)
187 | try:
188 | save(track['url'], file_path)
189 | except ssl.SSLError:
190 | print(f'SSL ERROR: {escaped_filename}, launching again...')
191 | try:
192 | save(track['url'], escaped_filename + '.mp3')
193 | except:
194 | print(f'Failed to save track after 2 tries [{i + 1}/{total}]')
195 | exit()
196 |
197 | print('\nUploading...')
198 | sys.stdout.flush()
199 | send_file(
200 | client, client.get_entity(VKMusicChannel),
201 | file_path,
202 | track['duration'], track['title'],
203 | track['artist'], filename
204 | )
205 |
206 | if not store_local:
207 | os.remove(file_path)
208 | print()
209 | sys.stdout.flush()
210 |
211 |
212 | if __name__ == '__main__':
213 | main()
214 | print('[Done] Finished uploading all the tracks')
215 |
--------------------------------------------------------------------------------