diff --git a/README.rst b/README.rst index 4f88815..d348fe6 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ 网易云音乐下载脚本 ################## -包含高音质地址生成算法,见光死的东西,所以你懂的。 +批量下载网易云音乐的歌曲,支持专辑和歌单,也可以下载歌词,补全歌曲标签和替换成清晰封面。 文件内容 ======== @@ -33,7 +33,7 @@ ID3 Tags 依赖 ---- -cloudmusic 里的代码仅依赖标准库。 +cloudmusic 里的代码仅依赖 Python 2.7 的标准库。 downloader 依赖 Python 的 eyed3 库来修改 ID3 Tags。 @@ -72,16 +72,21 @@ downloader 依赖 Python 的 eyed3 库来修改 ID3 Tags。 音质 ~~~~ -``-q 音质名`` 优先下载指定音质,音质名分别为 +``-q 音质名`` 优先下载指定音质,音质名意义如下: -* low 低音质,码率 96kbp/s -* medium 中等音质,码率 160kbp/s, -* high 高音质,码率 256kbp/s 或 320kpb/s,视歌曲而定 -* normal 普通音质,web 播放器的默认音质,相当于 medium,默认下载此音质 -* best 最佳音质,当前歌曲的最高码率音质,相当于 high +码率名 -有些歌曲不存在 high 音质,best、high、normal 都相当于 medium 音质。 -有些歌曲甚至连 medium 音质也没有,best、high、normal、medium 相当于 low 音质。 +* ``low`` 低音质,码率 96kbp/s。 +* ``medium`` 中等音质,码率 160kbp/s。 +* ``high`` 高音质,码率 320kpb/s,也有些歌曲是 256kbp/s 或 192kpb/s。 + +码率别名,其实就是码率名的映射 + +* ``normal`` 普通音质,web 播放器的默认音质,相当于 ``medium`` ,默认下载此音质。 +* ``best`` 最佳音质,当前歌曲的最高码率音质,相当于 ``high`` 。 + +有些歌曲不存在 ``high`` 码率,有些甚至连 ``medium`` 都没有,使用码率名会下载不到, +而使用码率别名会自动降级映射,不会出错。 歌词 ~~~~ @@ -106,3 +111,8 @@ downloader 依赖 Python 的 eyed3 库来修改 ID3 Tags。 ======== 在歌曲页面上显示各种音质的下载地址和歌词。 + +许可证 +====== + +GPLv3 diff --git a/cloudmusic/__init__.py b/cloudmusic/__init__.py index 0759e0d..52ef500 100644 --- a/cloudmusic/__init__.py +++ b/cloudmusic/__init__.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - from .song import Song, make_songs from .album import Album, make_albums from .playlist import Playlist, make_playlists diff --git a/cloudmusic/album.py b/cloudmusic/album.py index 171c348..3dd595b 100644 --- a/cloudmusic/album.py +++ b/cloudmusic/album.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import json from .utils import timestamp2datetime, read_url from .song import make_song_with_detail diff --git a/cloudmusic/artist.py b/cloudmusic/artist.py index a7f3c1e..96770ab 100644 --- a/cloudmusic/artist.py +++ b/cloudmusic/artist.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import json from .utils import read_url from .album import make_albums diff --git a/cloudmusic/hasher.py b/cloudmusic/hasher.py index d71e536..dc831d1 100644 --- a/cloudmusic/hasher.py +++ b/cloudmusic/hasher.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - from hashlib import md5 from base64 import b64encode @@ -13,17 +10,17 @@ def make_hash(dfsid): ''' key = '3go8&8ドル*3*3h0k(2)2' - key_codes = map(ord, list(key)) - fid_codes = map(ord, list(str(dfsid))) + key_codes = list(map(ord, list(key))) + fid_codes = list(map(ord, list(str(dfsid)))) hash_codes = [] - for i in xrange(len(fid_codes)): + for i in range(len(fid_codes)): hash_code = (fid_codes[i] ^ key_codes[i % len(key)]) & 0xFF hash_codes.append(hash_code) string = ''.join(map(chr, hash_codes)) - md5_digest = md5(string).digest() - base64_encoded = b64encode(md5_digest) + md5_digest = md5(string.encode('UTF-8')).digest() + base64_encoded = b64encode(md5_digest).decode('UTF-8') unescape_symbol = base64_encoded.replace('+', '-').replace('/', '_') return unescape_symbol diff --git a/cloudmusic/playlist.py b/cloudmusic/playlist.py index 42c018f..dfe5784 100644 --- a/cloudmusic/playlist.py +++ b/cloudmusic/playlist.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import json from .utils import read_url from .song import make_song_with_detail diff --git a/cloudmusic/song.py b/cloudmusic/song.py index b740221..d3707aa 100644 --- a/cloudmusic/song.py +++ b/cloudmusic/song.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import json from .utils import timestamp2datetime, read_url from .hasher import make_hash @@ -169,7 +166,7 @@ def album_cover_data(self): @property def album_cover_mimetype(self): - png_magic = '\x89PNG\x0d' + png_magic = b'\x89PNG\x0d' if self.album_cover_data.startswith(png_magic): return 'image/png' else: diff --git a/cloudmusic/utils.py b/cloudmusic/utils.py index e6635ed..9f6a171 100644 --- a/cloudmusic/utils.py +++ b/cloudmusic/utils.py @@ -1,9 +1,6 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import gzip -import urllib2 -import StringIO +from urllib import request +from io import BytesIO from datetime import datetime @@ -12,7 +9,7 @@ def timestamp2datetime(timestamp): def read_url(url): - opener = urllib2.build_opener() + opener = request.build_opener() opener.addheaders = [ ('User-Agent', 'android'), ('Referer', 'http://music.163.com/'), @@ -22,6 +19,6 @@ def read_url(url): resp = opener.open(url, timeout=3) content = resp.read() if resp.headers.get('content-encoding', None) == 'gzip': - content = gzip.GzipFile(fileobj=StringIO.StringIO(content), - mode='rb').read() + content = gzip.GzipFile(fileobj=BytesIO(content), + mode='rb').read().decode('UTF-8') return content diff --git a/downloader/__init__.py b/downloader/__init__.py index faa18be..e69de29 100644 --- a/downloader/__init__.py +++ b/downloader/__init__.py @@ -1,2 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- diff --git a/downloader/download.py b/downloader/download.py index 8297fcd..f5a84dc 100644 --- a/downloader/download.py +++ b/downloader/download.py @@ -1,9 +1,6 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import os -from id3 import fill_tags -from retrieve import retrieve_file +from .id3 import fill_tags +from .retrieve import retrieve_file def download_lyric(song, config): @@ -13,7 +10,7 @@ def download_lyric(song, config): return with open(filepath, 'w') as file: - file.write(song.lyric.encode('UTF-8')) + file.write(song.lyric) def download_audio(song, config): @@ -48,7 +45,7 @@ def download_songs(songs, config): def download_album(album, config): - song_folder = u'[专辑]' + album.title + song_folder = '[专辑]' + album.title album_folder = os.path.join(config['output'], song_folder) if not os.path.exists(album_folder): os.mkdir(album_folder) @@ -64,7 +61,7 @@ def download_albums(albums, config): def download_playlist(playlist, config): - song_folder = u'[歌单]' + playlist.title + song_folder = '[歌单]' + playlist.title playlist_folder = os.path.join(config['output'], song_folder) if not os.path.exists(playlist_folder): os.mkdir(playlist_folder) @@ -80,7 +77,7 @@ def download_playlists(playlists, config): def download_artist(artist, config): - album_folder = u'[艺术家]' + artist.name + album_folder = '[艺术家]' + artist.name artist_folder = os.path.join(config['output'], album_folder) if not os.path.exists(artist_folder): os.mkdir(artist_folder) diff --git a/downloader/id3.py b/downloader/id3.py index 9200dd5..7425fbe 100644 --- a/downloader/id3.py +++ b/downloader/id3.py @@ -1,27 +1,26 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import logging logger = logging.getLogger('eyed3.id3.frames') logger.setLevel(logging.ERROR) -import eyed3.mp3 +from eyed3.mp3 import Mp3AudioFile +from eyed3.id3 import frames +frames.Frame.__lt__ = lambda self, other: self.__class__.__name__ < other.__class__.__name__ def fill_tags(filename, song, config): - tag = eyed3.mp3.Mp3AudioFile(filename).tag + mp3file = Mp3AudioFile(filename) + mp3file.initTag() + tag = mp3file.tag tag.title = song.title tag.artist = song.artist tag.album = song.album_title tag.track_num = song.album_track_index, song.album_track_number tag.publisher = song.album_publisher - tag.recording_date = song.album_publish_datetime + tag.recording_date = song.album_publish_datetime.year tag.audio_file_url = song.main_url if config['cover']: - image = tag.images.get(u'') - image.image_data = song.album_cover_data - image.mime_type = song.album_cover_mimetype + tag.images.set(0, song.album_cover_data, song.album_cover_mimetype, '') tag.save() diff --git a/downloader/retrieve.py b/downloader/retrieve.py index c795a6d..1c1556b 100644 --- a/downloader/retrieve.py +++ b/downloader/retrieve.py @@ -1,13 +1,10 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import sys from time import time -import urllib2 +from urllib import request def urlretrieve(remote_url, local_url, reporthook=None): - opener = urllib2.build_opener() + opener = request.build_opener() opener.addheaders = [ ('User-Agent', 'stagefright/1.2(proxy)'), ('Referer', 'http://music.163.com/api/'), @@ -46,7 +43,7 @@ def process_func(block_count, block_size, total_size): percent_text = str(percent) + '%' bar = (percent * 20) / 100 - bar_text = '[%-20s]' % ('=' * bar) + bar_text = '[{:20s}]'.format('=' * int(bar)) speed = downloaded_size / (time() - start_time) / 1024.0 speed_text = '%.2fK/s' % speed diff --git a/nmdown.py b/nmdown.py index d8a3ecc..19c424d 100755 --- a/nmdown.py +++ b/nmdown.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 import os import re @@ -12,20 +11,20 @@ def print_songs(songs, indent=0): for song in songs: if indent: - print ' ' * indent, - print song.id, song.title, song.best_quality_mp3_url - print + print(' ' * indent, end='') + print(song.id, song.title, song.best_quality_mp3_url) + print() def print_albums(albums): for album in albums: - print album.id, album.title + print(album.id, album.title) print_songs(album.songs, 2) def print_playlists(playlists): for playlist in playlists: - print playlist.id, playlist.title + print(playlist.id, playlist.title) print_songs(playlist.songs, 2)

AltStyle によって変換されたページ (->オリジナル) /