forked from heqin-zhu/music-recover
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdecrypt.py
207 lines (189 loc) · 7.69 KB
/
decrypt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#coding : utf-8
import os
import sys
import glob
import requests
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
DESDIR = '../Decrypted'
LRCDIR = os.path.join(DESDIR, 'lyric')
MSCDIR = os.path.join(DESDIR, 'music')
API = 'https://api.imjad.cn/cloudmusic/?'
# two args: id type
# type=song, lyric, comments, detail, artist, album, search
# eg API = 'https://api.imjad.cn/cloudmusic/?type=song&id=1234132' download music
hasModu = False
try:
from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3, APIC, ID3NoHeaderError
from mutagen.mp3 import MP3, HeaderNotFoundError
from mutagen import MutagenError
hasModu = True
except:
pass
def safeprint(s):
'''deal with invalid encoded filename'''
try:
print(s)
except:
print(repr(s)[1:-1])
class netease_music:
def __init__(self, path=''):
'''path is the directory that contains Music files(cached)'''
if path == '':
path = input('init: Cannot find the path of cached netease_music')
self.path = path
safeprint('[+] Current Path: ' + path)
os.chdir(path)
self.files = glob.glob('*.uc') + glob.glob('*.uc!')
self.id_mp = {}
self.title = {}
self.artist = {}
self.album = {}
self.cover = {}
for i in self.files:
self.id_mp[self.getId(i)] = i
if not os.path.exists(DESDIR):
os.mkdir(DESDIR)
if not os.path.exists(LRCDIR):
os.mkdir(LRCDIR)
if not os.path.exists(MSCDIR):
os.mkdir(MSCDIR)
# import re
# self.nameXpath ='//div[@class="tit"]/em[@class="f-ff2"]/text()'
# self.lrcSentencePt=re.compile(r'\[\d+:\d+\.\d+\](.*?)\\n') # wrong (r'\[\d+,\d+\](\(\d+,\d+\)(\w))+\n')
def getId(self, name):
return name[:name.find('-')]
def getInfoFromWeb(self, musicId):
dic = {}
url = API+'type=detail&id=' + musicId
try:
info = requests.get(url).json()['songs'][0]
dic['artist'] = [info['ar'][0]['name']]
dic['title'] = [info['name']]
dic['cover'] = [info['al']['picUrl']]
dic['album'] = [info['al']['name']]
return dic
except Exception as e:
print('getInfoFromWeb: list index out of range.')
def getInfoFromFile(self, path):
if not os.path.exists(path):
safeprint('getInfoFromFile: Can not find file ' + path)
return {}
elif hasModu:
try:
fileinfo = MP3(path)
return dict(MP3(path, ID3=EasyID3))
except HeaderNotFoundError as e:
print('getInfoFromFile: Fail to read the file tags.')
else:
print('getInfoFromFile: You can use pip3 to install mutagen or connet to the Internet')
raise Exception('getInfoFromFile: Failed to get info of ' + path)
def getPath(self, dic, musicId):
try:
title = dic['title'][0]
artist = dic['artist'][0]
album = dic['album'][0]
cover = dic['cover'][0]
if artist in title:
title = title.replace(artist, '').strip()
name = (artist + ' - ' + title)
for i in '>?*/\:"|<':
name = name.replace(i,'-') # form valid file name
self.id_mp[musicId] = name
self.title[musicId] = title
self.artist[musicId] = artist
self.album[musicId] = album
self.cover[musicId] = cover
#print('''{{title: "{title}",artist: "{artist}",mp3: "http://ounix1xcw.bkt.clouddn.com/{name}.mp3",cover: "{cover}",}},'''\
#.format(title = title,name = name,artist=artist,cover=dic['cover'][0]))
return os.path.join(MSCDIR, name + '.mp3')
except Exception as e: # IndexError, TypeError
return os.path.join(MSCDIR, musicId + '.mp3')
def decrypt(self, cachePath):
musicId = self.getId(cachePath)
idpath = os.path.join(MSCDIR, musicId + '.mp3')
try: # from web
dic = self.getInfoFromWeb(musicId)
path = self.getPath(dic,musicId)
if os.path.exists(path): return
with open(path,'wb') as f:
f.write(bytes(self._decrypt(cachePath)))
except Exception as e: # from file
print(e)
print ("decrypt: Trying get tags from file..")
if not os.path.exists(idpath):
with open(idpath,'wb') as f:
f.write(bytes(self._decrypt(cachePath)))
dic = self.getInfoFromFile(idpath)
path = getPath(dic,musicId)
if os.path.exists(path):
os.remove(idpath)
return
os.rename(idpath, path)
def _decrypt(self,cachePath):
with open(cachePath, 'rb') as f:
btay = bytearray(f.read())
for i, j in enumerate(btay):
btay[i] = j ^ 0xa3
return btay
def getLyric(self, musicId):
name = self.id_mp[musicId]
url = API + 'type=lyric&id=' + musicId
url2 = 'https://music.163.com/api/song/lyric?id='+ musicId +'&lv=1&kv=1&tv=-1'
try:
lrc = requests.get(url).json()['lrc']['lyric']
if lrc=='':
lrc = requests.get(url2).json()['lrc']['lyric']
if lrc=='':
raise Exception('')
file = os.path.join(LRCDIR, name + '.lrc')
if not os.path.exists(file):
with open(file, 'w', encoding='utf8') as f:
f.write(str(lrc))
except Exception as e:
print(e,end='')
safeprint('getLyric: Failed to get lyric of music '+name)
def getMusic(self):
for ct, cachePath in enumerate(self.files):
self.decrypt(cachePath)
musicId = self.getId(cachePath)
mfilename = self.id_mp[musicId] + '.mp3'
mfilepath = os.path.join(MSCDIR, mfilename)
try:
tags = EasyID3(mfilepath)
tags['title'] = self.title[musicId]
tags['album'] = self.album[musicId]
tags['artist'] = self.artist[musicId]
tags.save()
except MutagenError:
print ('getMusic: Loading EasyID3 tags failed.')
try:
albumcover = urlopen(self.cover[musicId])
try:
audio = ID3(mfilepath)
audio['APIC'] = APIC(
encoding=3,
mime='image/jpeg',
type=3, desc=u'Cover',
data=albumcover.read()
)
albumcover.close()
audio.save()
except ID3NoHeaderError:
print ('getMusic: Loading ID3 tags failed.')
except KeyError as e:
print('Error: cover url is not in the dictionary')
except HTTPError as e:
print('Error code: ', e.code)
except URLError as e:
print ('Error code: ', e.reason)
print('[{}]'.format(ct+1).ljust(5) + '[{}]'.format(musicId).ljust(12) + mfilename)
self.getLyric(musicId)
if __name__ == '__main__':
if len(sys.argv) > 1:
path = sys.argv[1].strip()
else:
path = os.path.join(os.getcwd(), 'Music1')
handler = netease_music(path)
handler.getMusic()