Compare commits
	
		
			No commits in common. "e9bbb3809fab64b99d12cd72f14b21bb4279cb12" and "ca698b9fbc543d406fc0a1d3b417183b8248d4ce" have entirely different histories.
		
	
	
		
			e9bbb3809f
			...
			ca698b9fbc
		
	
		
							
								
								
									
										12
									
								
								ncm/api.py
								
								
								
								
							
							
						
						
									
										12
									
								
								ncm/api.py
								
								
								
								
							|  | @ -69,13 +69,21 @@ class CloudApi(object): | |||
|     def get_program_url(self, program, encode_type="aac", level="standard"): | ||||
|         """ | ||||
|         Get the download url of the program | ||||
|         :param program | ||||
|         :param program: | ||||
|         :param encode_type: | ||||
|         :param level: | ||||
|         :return: | ||||
|         """ | ||||
|         id = program['mainSong']['id'] | ||||
|         return self.get_song_url(id) | ||||
|         url = program_download_url | ||||
|         payload = { | ||||
|             'ids': [id], | ||||
|             'csrf_token': '', | ||||
|             'encodeType': encode_type, | ||||
|             'level': level | ||||
|         } | ||||
|         result = self.post_request(url, payload) | ||||
|         return result['data'][0]['url'] | ||||
| 
 | ||||
|     def get_album_songs(self, album_id): | ||||
|         """ | ||||
|  |  | |||
|  | @ -12,19 +12,19 @@ headers = { | |||
|     'Host': 'music.163.com', | ||||
|     'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36', | ||||
|     'Referer': 'http://music.163.com', | ||||
|     'Cookie': 'appver=2.0.2; _ntes_nuid={}; NMTID={};'.format( | ||||
|     'Cookie': 'appver=2.0.2; _ntes_nuid={}; NMTID={}'.format( | ||||
|         ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)), | ||||
|         ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32))) | ||||
| } | ||||
| song_download_url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token=' | ||||
| program_download_url = 'http://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=' | ||||
| program_download_url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=' | ||||
| 
 | ||||
| def get_song_url(song_id): | ||||
|     return 'http://music.163.com/api/song/detail/?ids=[{}]'.format(song_id) | ||||
| 
 | ||||
| 
 | ||||
| def get_program_url(program_id): | ||||
|     return 'http://music.163.com/weapi/dj/program/detail?csrf_token=' | ||||
|     return 'https://music.163.com/weapi/dj/program/detail?csrf_token=' | ||||
| 
 | ||||
| 
 | ||||
| def get_album_url(album_id): | ||||
|  | @ -37,8 +37,3 @@ def get_artist_url(artist_id): | |||
| 
 | ||||
| def get_playlist_url(playlist_id): | ||||
|     return 'http://music.163.com/api/v6/playlist/detail?id={}'.format(playlist_id) | ||||
| def add_cookies(key, value): | ||||
|     cookie = headers.get('Cookie') | ||||
|     cookie += '{}={};'.format(key, value) | ||||
|     print(cookie) | ||||
|     headers['Cookie'] = cookie | ||||
|  | @ -56,12 +56,12 @@ def download_song_by_song(song, download_folder, sub_folder=True, program=False) | |||
| 
 | ||||
|     # download song | ||||
|     if program: | ||||
|         song_url = api.get_program_url(song, level="standard") | ||||
|         song_url = api.get_program_url(song) | ||||
|     else: | ||||
|         song_url = api.get_song_url(song_id) | ||||
| 
 | ||||
|     if song_url is None: | ||||
|         print('Song <<{}>> is not available due to copyright issue! please set cookie.'.format(song_name)) | ||||
|         print('Song <<{}>> is not available due to copyright issue!'.format(song_name)) | ||||
|         return | ||||
|     is_already_download = download_file(song_url, song_file_name, song_download_folder) | ||||
|     if is_already_download: | ||||
|  | @ -70,15 +70,12 @@ def download_song_by_song(song, download_folder, sub_folder=True, program=False) | |||
| 
 | ||||
|     # download cover | ||||
|     if program: | ||||
|         cover_url = song['coverUrl'] | ||||
|         cover_url = song['blurCoverUrl'] | ||||
|     else: | ||||
|         cover_url = song['album']['blurPicUrl'] | ||||
|         cover_url = song['album']['coverUrl'] | ||||
| 
 | ||||
|     if cover_url is None: | ||||
|         if program: | ||||
|             cover_url = song['mainSong']['album']['picUrl'] | ||||
|         else: | ||||
|             cover_url = song['album']['picUrl'] | ||||
|         cover_url = song['album']['picUrl'] | ||||
|     cover_file_name = 'cover_{}.jpg'.format(song_id) | ||||
|     download_file(cover_url, cover_file_name, song_download_folder) | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ def resize_img(file_path, max_size=(640, 640), quality=90): | |||
|         img.save(file_path, quality=quality) | ||||
| 
 | ||||
| 
 | ||||
| def add_metadata_to_song(file_path, cover_path, song, is_program=False): | ||||
| def add_metadata_to_song(file_path, cover_path, song, is_program = False): | ||||
|     # If no ID3 tags in mp3 file | ||||
|     try: | ||||
|         audio = MP3(file_path, ID3=ID3) | ||||
|  | @ -53,15 +53,20 @@ def add_metadata_to_song(file_path, cover_path, song, is_program=False): | |||
|     ) | ||||
|     # add artist name | ||||
|     if is_program: | ||||
|         art_name = song['dj']['nickname'] | ||||
|     else: | ||||
|         art_name = song['artists'][0]['name'] | ||||
|     id3.add( | ||||
|         TPE1( | ||||
|             encoding=3, | ||||
|             text=art_name | ||||
|         id3.add( | ||||
|             TPE1( | ||||
|                 encoding=3, | ||||
|                 text=song['dj']['nickname'] | ||||
|             ) | ||||
|         ) | ||||
|     ) | ||||
|     else: | ||||
|         id3.add( | ||||
|             TPE1( | ||||
|                 encoding=3, | ||||
|                 text=song['artists'][0]['name'] | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|     # add song name | ||||
|     id3.add( | ||||
|         TIT2( | ||||
|  | @ -71,21 +76,25 @@ def add_metadata_to_song(file_path, cover_path, song, is_program=False): | |||
|     ) | ||||
|     # add album name | ||||
|     if is_program: | ||||
|         album_name = song['dj']['brand'] | ||||
|     else: | ||||
|         album_name = song['album']['name'] | ||||
|     id3.add( | ||||
|         TALB( | ||||
|             encoding=3, | ||||
|             text=album_name | ||||
|         id3.add( | ||||
|             TALB( | ||||
|                 encoding=3, | ||||
|                 text=song['dj']['brand'] | ||||
|             ) | ||||
|         ) | ||||
|     ) | ||||
|     # add track no | ||||
|     else: | ||||
|         id3.add( | ||||
|             TALB( | ||||
|                 encoding=3, | ||||
|                 text=song['album']['name'] | ||||
|             ) | ||||
|         ) | ||||
|     #add track no | ||||
|     if not is_program: | ||||
|         id3.add( | ||||
|             TRCK( | ||||
|                 encoding=3, | ||||
|                 text="%s/%s" % (song['no'], song['album']['size']) | ||||
|                 text="%s/%s" %(song['no'],song['album']['size']) | ||||
|             ) | ||||
|         ) | ||||
|     # programs doesn't have a valid album info. | ||||
|  |  | |||
							
								
								
									
										158
									
								
								ncm/start.py
								
								
								
								
							
							
						
						
									
										158
									
								
								ncm/start.py
								
								
								
								
							|  | @ -1,21 +1,20 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import argparse | ||||
| import os | ||||
| import tkinter as tk | ||||
| from tkinter import messagebox, ttk | ||||
| from urllib.parse import urlparse, parse_qs | ||||
| 
 | ||||
| from urllib.parse import urlparse, parse_qs | ||||
| from ncm import config | ||||
| from ncm.api import CloudApi | ||||
| from ncm.constants import add_cookies | ||||
| from ncm.constants import headers | ||||
| from ncm.downloader import get_song_info_by_id | ||||
| from ncm.downloader import download_song_by_id | ||||
| from ncm.downloader import download_song_by_song | ||||
| from ncm.downloader import format_string | ||||
| from ncm.downloader import get_song_info_by_id | ||||
| 
 | ||||
| # load the config first | ||||
| config.load_config() | ||||
| api = CloudApi() | ||||
| 
 | ||||
| 
 | ||||
| def download_hot_songs(artist_id): | ||||
|     songs = api.get_hot_songs(artist_id) | ||||
|     folder_name = format_string(songs[0]['artists'][0]['name']) + ' - hot50' | ||||
|  | @ -25,6 +24,7 @@ def download_hot_songs(artist_id): | |||
|         print('{}: {}'.format(i + 1, song['name'])) | ||||
|         download_song_by_song(song, folder_path, False) | ||||
| 
 | ||||
| 
 | ||||
| def download_album_songs(album_id): | ||||
|     songs = api.get_album_songs(album_id) | ||||
|     folder_name = format_string(songs[0]['album']['name']) + ' - album' | ||||
|  | @ -33,12 +33,14 @@ def download_album_songs(album_id): | |||
|         print('{}: {}'.format(i + 1, song['name'])) | ||||
|         download_song_by_song(song, folder_path, False) | ||||
| 
 | ||||
| 
 | ||||
| def download_program(program_id): | ||||
|     program = api.get_program(program_id) | ||||
|     folder_name = format_string(program['dj']['brand']) + ' - program' | ||||
|     folder_name = format_string(program['dj']['brand']) + ' - album' | ||||
|     folder_path = os.path.join(config.DOWNLOAD_DIR, folder_name) | ||||
|     download_song_by_song(program, folder_path, False, True) | ||||
| 
 | ||||
| 
 | ||||
| def download_playlist_songs(playlist_id): | ||||
|     songs, playlist_name = api.get_playlist_songs(playlist_id) | ||||
|     folder_name = format_string(playlist_name) + ' - playlist' | ||||
|  | @ -48,126 +50,44 @@ def download_playlist_songs(playlist_id): | |||
|         print('{}: {}'.format(i + 1, song_detail['name'])) | ||||
|         download_song_by_song(song_detail, folder_path, False) | ||||
| 
 | ||||
| 
 | ||||
| def get_parse_id(song_id): | ||||
|     # Parse the url | ||||
|     if song_id.startswith('http'): | ||||
|         # Not allow fragments, we just need to parse the query string | ||||
|         return parse_qs(urlparse(song_id, allow_fragments=False).query)['id'][0] | ||||
|     return song_id | ||||
| 
 | ||||
| def handle_download(option_var): | ||||
|     option = option_var.get() | ||||
|     print("Option:", option) | ||||
|     if option == "song_id": | ||||
|         song_id = song_id_entry.get() | ||||
|         if song_id: | ||||
|             download_song_by_id(get_parse_id(song_id), config.DOWNLOAD_DIR) | ||||
|     elif option == "song_ids": | ||||
|         song_ids = song_ids_entry.get().split() | ||||
|         for sid in song_ids: | ||||
|             download_song_by_id(get_parse_id(sid), config.DOWNLOAD_DIR) | ||||
|     elif option == "artist_id": | ||||
|         artist_id = artist_id_entry.get() | ||||
|         if artist_id: | ||||
|             download_hot_songs(get_parse_id(artist_id)) | ||||
|     elif option == "album_id": | ||||
|         album_id = album_id_entry.get() | ||||
|         if album_id: | ||||
|             download_album_songs(get_parse_id(album_id)) | ||||
|     elif option == "program_id": | ||||
|         program_id = program_id_entry.get() | ||||
|         if program_id: | ||||
|             download_program(get_parse_id(program_id)) | ||||
|     elif option == "playlist_id": | ||||
|         playlist_id = playlist_id_entry.get() | ||||
|         if playlist_id: | ||||
|             download_playlist_songs(get_parse_id(playlist_id)) | ||||
| 
 | ||||
|     messagebox.showinfo("完成", "已完成") | ||||
| 
 | ||||
| def set_cookies(cookies_entry): | ||||
|     cookies = cookies_entry.get().split() | ||||
|     for cookie in cookies: | ||||
|         key, value = cookie.replace("\n", "").replace(" ", "").replace("\t", "").split('=') | ||||
|         add_cookies(key, value) | ||||
| 
 | ||||
| def open_settings_window(): | ||||
|     settings_window = tk.Toplevel(root) | ||||
|     settings_window.title("设置") | ||||
| 
 | ||||
|     # 设置窗口大小 | ||||
|     settings_width = 340 | ||||
|     settings_height = 110 | ||||
|     center_window(settings_window, settings_width, settings_height) | ||||
| 
 | ||||
|     tk.Label(settings_window, text="User-Agent:").grid(row=0, column=0, padx=5, pady=5) | ||||
|     user_agent_entry = tk.Entry(settings_window) | ||||
|     user_agent_entry.grid(row=0, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     save_button = tk.Button(settings_window, text="设置UA", command=lambda: {'User-Agent': user_agent_entry}) | ||||
|     save_button.grid(row=0, column=2, padx=5, pady=10) | ||||
| 
 | ||||
|     tk.Label(settings_window, text="Cookies (k=v):").grid(row=1, column=0, padx=5, pady=5) | ||||
|     cookies_entry = tk.Entry(settings_window) | ||||
|     cookies_entry.grid(row=1, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     save_button = tk.Button(settings_window, text="设置Cookie", command=lambda: set_cookies(cookies_entry)) | ||||
|     save_button.grid(row=1, column=2, padx=5, pady=10) | ||||
| 
 | ||||
|     # 设置焦点到设置窗口 | ||||
|     settings_window.focus_force() | ||||
| 
 | ||||
| def center_window(window, width, height): | ||||
|     screen_width = window.winfo_screenwidth() | ||||
|     screen_height = window.winfo_screenheight() | ||||
|     x = (screen_width - width) // 2 | ||||
|     y = (screen_height - height) // 2 | ||||
|     window.geometry(f'{width}x{height}+{x}+{y}') | ||||
| 
 | ||||
| def main(): | ||||
|     global root, song_id_entry, song_ids_entry, artist_id_entry, album_id_entry, program_id_entry, playlist_id_entry | ||||
|     parser = argparse.ArgumentParser(description='Welcome to netease cloud music downloader!') | ||||
|     parser.add_argument('-s', metavar='song_id', dest='song_id', | ||||
|                         help='Download a song by song_id') | ||||
|     parser.add_argument('-ss', metavar='song_ids', dest='song_ids', nargs='+', | ||||
|                         help='Download a song list, song_id split by space') | ||||
|     parser.add_argument('-hot', metavar='artist_id', dest='artist_id', | ||||
|                         help='Download an artist hot 50 songs by artist_id') | ||||
|     parser.add_argument('-a', metavar='album_id', dest='album_id', | ||||
|                         help='Download an album all songs by album_id') | ||||
|     parser.add_argument('-dj', metavar='program_id', dest='program_id', | ||||
|                         help='Download a program by program_id') | ||||
|     parser.add_argument('-p', metavar='playlist_id', dest='playlist_id', | ||||
|                         help='Download a playlist all songs by playlist_id') | ||||
|     args = parser.parse_args() | ||||
|     if args.song_id: | ||||
|         download_song_by_id(get_parse_id(args.song_id), config.DOWNLOAD_DIR) | ||||
|     elif args.song_ids: | ||||
|         for song_id in args.song_ids: | ||||
|             download_song_by_id(get_parse_id(song_id), config.DOWNLOAD_DIR) | ||||
|     elif args.artist_id: | ||||
|         download_hot_songs(get_parse_id(args.artist_id)) | ||||
|     elif args.album_id: | ||||
|         download_album_songs(get_parse_id(args.album_id)) | ||||
|     elif args.playlist_id: | ||||
|         download_playlist_songs(get_parse_id(args.playlist_id)) | ||||
|     elif args.program_id: | ||||
|         download_program(get_parse_id(args.program_id)) | ||||
| 
 | ||||
|     root = tk.Tk() | ||||
|     root.title("网易云音乐下载器") | ||||
| 
 | ||||
|     # 设置窗口大小 | ||||
|     window_width = 260 | ||||
|     window_height = 250 | ||||
|     center_window(root, window_width, window_height) | ||||
| 
 | ||||
|     # 下载选项 | ||||
|     option_var = tk.StringVar(value="song_id") | ||||
|     options = ["Song ID", "Song IDs", "Artist ID", "Album ID", "Program ID", "Playlist ID"] | ||||
|     for idx, option in enumerate(options): | ||||
|         rb = ttk.Radiobutton(root, text=option, variable=option_var, value=option.lower().replace(" ", "_")) | ||||
|         rb.grid(row=idx+1, column=0, sticky="w", padx=5, pady=5) | ||||
| 
 | ||||
|     # 输入框 | ||||
|     song_id_entry = tk.Entry(root) | ||||
|     song_id_entry.grid(row=1, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     song_ids_entry = tk.Entry(root) | ||||
|     song_ids_entry.grid(row=2, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     artist_id_entry = tk.Entry(root) | ||||
|     artist_id_entry.grid(row=3, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     album_id_entry = tk.Entry(root) | ||||
|     album_id_entry.grid(row=4, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     program_id_entry = tk.Entry(root) | ||||
|     program_id_entry.grid(row=5, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     playlist_id_entry = tk.Entry(root) | ||||
|     playlist_id_entry.grid(row=6, column=1, padx=5, pady=5) | ||||
| 
 | ||||
|     # 设置页面按钮 | ||||
|     settings_button = tk.Button(root, text="设置", command=open_settings_window) | ||||
|     settings_button.grid(row=7, column=0, columnspan=1, pady=10) | ||||
| 
 | ||||
|     # 下载按钮 | ||||
|     download_button = tk.Button(root, text="下载", command=lambda: handle_download(option_var)) | ||||
|     download_button.grid(row=7, column=1, columnspan=2, pady=10) | ||||
| 
 | ||||
|     root.mainloop() | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     main() | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| requests>=2.17.3 | ||||
| pycryptodomex | ||||
| mutagen>=1.38.0 | ||||
| Pillow==9.5.0 | ||||
| Pillow>=4.3.0 | ||||
		Loading…
	
		Reference in New Issue