当前位置:网站首页>DownMusic summary record
DownMusic summary record
2022-08-03 01:32:00 【Y6blNU1L】
# -*- coding: utf-8 -*-
import base64
import requests
import os
import json
from pyDes import des, PAD_PKCS5, CBC
import threading
# 加解密工具
def decryptAndSetCookie(text: str):
replace = text.replace("-", "").replace("|", "")
if len(replace) < 10 or replace.find("%") == -1:
return False
split = replace.split("%")
key = split[0]
qq = str(decryptDES(split[1], key[0:8]), "utf-8")
if len(qq) < 8:
qq += "QMD"
mkey = str(decryptDES(key, qq[0:8]), "utf-8")
return mkey, qq # 用对象的encrypt方法加密
# des解密
def decryptDES(strs: str, key: str): return des(
key, CBC, key, padmode=PAD_PKCS5).decrypt(base64.b64decode(str(strs)))
# des加密
def encryptDES(text: str, key: str): return str(base64.b64encode(
des(key, CBC, key, padmode=PAD_PKCS5).encrypt(text)), 'utf-8')
# 加密字符串
def encryptText(text: str, qq: str):
key = ("QMD"+qq)[0:8]
return encryptDES(text, key)
# 解密字符串
def decryptText(text: str, qq: str): return str(decryptDES(
text.replace("-", ""), ("QMD" + qq)[0:8]), 'utf-8')
def getHead():
return {
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
'content-type': 'application/json; charset=UTF-8',
"referer": "https://y.qq.com/portal/profile.html"
}
sess = requests.Session()
def buildSearchContent(song='', page=1, page_per_num=100):
return {
"comm": {
"ct": "19", "cv": "1845"},
"music.search.SearchCgiService": {
"method": "DoSearchForQQMusicDesktop",
"module": "music.search.SearchCgiService",
"param": {
"query": song, "num_per_page": page_per_num, "page_num": page}
}
}
def searchMusic(key="", page=1):
# base url
url = "https://u.y.qq.com/cgi-bin/musicu.fcg"
# base data content from qqmusic pc-client-apps
data = buildSearchContent(key, page)
data = json.dumps(data, ensure_ascii=False)
data = data.encode('utf-8')
res = sess.post(url, data, headers=getHead())
jsons = res.json()
# 开始解析QQMusic search results
res = jsons['music.search.SearchCgiService']['data']
list = res['body']['song']['list']
meta = res['meta']
# 数据清洗,Remove redundant data in search results
list_clear = []
for i in list:
list_clear.append({
'album': i['album'],
'docid': i['docid'],
'id': i['id'],
'mid': i['mid'],
'name': i['title'],
'singer': i['singer'],
'time_public': i['time_public'],
'title': i['title'],
'file': i['file'],
})
# rebuild json
# list_clear: Search out list of songs
# {
# size 搜索结果总数
# next The next search page -1Said the search results have been exactly
# cur The search results page
# }
return list_clear, {
'size': meta['sum'],
'next': meta['nextpage'],
'cur': meta['curpage']
}
def getCookie():
uid = "822a3b85-a5c9-438e-a277-a8da412e8265"
systemVersion = "1.7.2"
versionCode = "76"
deviceBrand = "360"
deviceModel = "QK1707-A01"
appVersion = "7.1.2"
encIP = encryptText(
f'{
uid}{
deviceModel}{
deviceBrand}{
systemVersion}{
appVersion}{
versionCode}', "F*ckYou!")
u = 'http://8.136.185.193/api/Cookies'
d = f'\{
{"appVersion":"{
appVersion}","deviceBrand":"{
deviceBrand}","deviceModel":"{
deviceModel}","ip":"{
encIP}","systemVersion":"{
systemVersion}","uid":"{
uid}","versionCode":"{
versionCode}"\}}'.replace(
"\\", "")
ret = sess.post(u, d, headers={
'Content-Type': 'application/json; charset=UTF-8'
})
return ret.text
def getDownloadLink(fileName):
u = 'http://8.136.185.193/api/MusicLink/link'
d = f'"{
encryptText(fileName, mqq_)}"'
ret = sess.post(
u, d, headers={
"Content-Type": "application/json;charset=utf-8"
})
return ret.text
def getMusicFileName(code, mid, format): return f'{
code}{
mid}.{
format}'
def parseSectionByNotFound(filename, songmid):
global mqq_
global mkey_
u = 'https://u.y.qq.com/cgi-bin/musicu.fcg'
d = {
"comm": {
"ct": "19", "cv": "1777"}, "queryvkey": {
"method": "CgiGetVkey", "module": "vkey.GetVkeyServer", "param": {
"uin": mqq_,
"guid": "QMD50",
"referer": "y.qq.com",
"songtype": [1],
"filename": [filename], "songmid": [songmid]
}}}
d = json.dumps(d, ensure_ascii=False)
d = sess.post(u, d, headers={
'referer': 'https://y.qq.com/portal/profile.html',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
'cookie': f'qqmusic_key={
mkey_};qqmusic_uin={
mqq_};',
'content-type': 'application/json; charset=utf-8'
})
vkey = d.json()['queryvkey']['data']['midurlinfo'][0]['purl']
u = f'http://ws.stream.qqmusic.qq.com/{
vkey}&fromtag=140'
return u
mkey_ = ""
mqq_ = ""
def downSingle(it):
global download_home
# prepare
localFile = f"{
it['singer']} - {
it['title']}.{
it['extra']}"
my_path = download_home+it['singer']+'/'
my_path = f"{
my_path}{
it['album']}"
if not os.path.exists(my_path):
os.mkdir(f"{
my_path}")
localFile = os.path.join(my_path, f"{
localFile}")
if os.path.exists(localFile):
if os.path.getsize(localFile) == int(it['size']):
print(f"本地已下载,跳过下载 [{
it['album']} / {
localFile}].")
return True
else:
print(
f"Local file size discrepancy: {
os.path.getsize(localFile)}/{
int(it['size'])},Began to cover to download [{
localFile}].")
file = getMusicFileName(
it['prefix'], it['mid'], it['extra'])
log = f"{
it['singer']} - {
it['title']} [{
it['notice']}] {
round(int(it['size'])/1024/1024,2)}MB - {
file}"
print(f'正在下载 | {
it["album"]} / {
log}')
link = getDownloadLink(file)
if link.find('qqmusic.qq.com') == -1:
if link.find('"title":"Not Found"') != -1:
# Start the second resolution
link = parseSectionByNotFound(file, it['songmid'])
else:
print(f"Parsing song download address failure!{
log}")
return False
f = sess.get(link)
with open(localFile, 'wb') as code:
code.write(f.content)
code.flush()
return True
def _main(target="周杰伦"):
global mkey_
global mqq_
global download_home
global dualThread
print("==== welcome to QQMusic digit High Quality Music download center ====")
my_path = download_home+target+'/'
if not os.path.exists(my_path):
os.mkdir(f"{
my_path}")
cookie = getCookie()
mkey, qq = decryptAndSetCookie(cookie)
mkey_ = mkey
mqq_ = qq
# According to the file name for a download link
# getDownloadLink("RS01003w2xz20QlUZt.flac")
# filename = "ID9TZr-ensC/-rJ2t6-atFsm+sRG+2S6CqS"
# filename = decryptText(filename, qq)
# # 解密后 RS01 003w2xz20QlUZt . flac
page = 1
while True:
(list, meta) = searchMusic(target, page)
if meta['next'] != -1:
add = 1
span = " "
songs = []
for i in list:
singer = i['singer'][0]['name']
if singer != target:
# print(f"{singer} not is {target}")
continue
if add > 9:
span = " "
if add > 99:
span = ""
id = i["file"]
# Batch download don't need to select quality Directly to resolve to the highest quality 枚举
code = ""
format = ""
qStr = ""
fsize = 0
mid = id['media_mid']
if int(id['size_hires']) != 0:
# High resolution nondestructive quality
code = "RS01"
format = "flac"
qStr = "高解析无损 Hi-Res"
fsize = int(id['size_hires'])
elif int(id['size_flac']) != 0:
isEnc = False # This code is reverse out 暂时无效
if(isEnc):
code = "F0M0"
format = "mflac"
else:
code = "F000"
format = "flac"
qStr = "无损品质 FLAC"
fsize = int(id['size_flac'])
elif int(id['size_320mp3']) != 0:
code = "M800"
format = "mp3"
qStr = "超高品质 320kbps"
fsize = int(id['size_320mp3'])
elif int(id['size_192ogg']) != 0:
isEnc = False # This code is reverse out 暂时无效
if(isEnc):
code = "O6M0"
format = "mgg"
else:
code = "O600"
format = "ogg"
qStr = "高品质 OGG"
fsize = int(id['size_192ogg'])
elif int(id['size_128mp3']) != 0:
isEnc = False # This code is reverse out 暂时无效
if(isEnc):
code = "O4M0"
format = "mgg"
else:
code = "M500"
format = "mp3"
qStr = "标准品质 128kbps"
fsize = int(id['size_128mp3'])
elif int(id['size_96aac']) != 0:
code = "C400"
format = "m4a"
qStr = "低品质 96kbps"
fsize = int(id['size_96aac'])
albumName = str(i["album"]['title']).strip(" ")
if albumName == '':
albumName = "Unclassified album"
songs.append({
'prefix': code,
'extra': format,
'notice': qStr,
'mid': mid,
'songmid': i['mid'],
'size': fsize,
'title': f'{
i["title"]}',
'singer': f'{
singer}',
'album': albumName})
time_publish = i["time_public"]
if time_publish == '':
time_publish = "0000-00-00"
print(
f'{
add} {
span}{
time_publish} {
singer} - {
i["title"]}')
add += 1
willDownAll = False
while True:
print(
f"\n获取列表成功.当前第{
page}页,共{
meta['size']}条搜索结果.\nThe next page inputn\nPrevious inputp\nA key to download all the songs enter on this pagea\nIf you want to download a song,Please enter the song in front of the serial number.\nModify search keyword inputs\n请输入:", end='')
inputKey = input()
if inputKey == "n":
break
elif inputKey == "s":
print('\033c', end='')
print("Please enter a new search keywords:", end='')
_main(input())
return
elif inputKey == 'a':
# Download all the songs on this page
willDownAll = True
elif inputKey == 'p':
page -= 2
if page + 1 < 1:
page = 0
break
if willDownAll:
thList = []
for mp3 in songs:
th = threading.Thread(target=downSingle, args=(mp3,))
thList.append(th)
th.start()
if len(thList) == dualThread:
while len(thList) > 0:
thList.pop().join()
# downSingle(mp3)
while len(thList) > 0:
thList.pop().join()
willDownAll = False
else:
op = -1
try:
op = int(inputKey)
except:
print("输入无效字符,请重新输入.")
continue
it = songs[op-1]
downSingle(it)
print("下载完成!")
page += 1
else:
break
print()
# Download the file to save where
# /music/Is your custom folder name 随便指定 会自动创建
download_home = "./"
# 多线程下载 线程数量
dualThread = 16
_main()
边栏推荐
- VMware workstation program starts slowly
- CKAN教程之在 AWS 上部署 CKAN 应用程序
- 【斯坦福计网CS144项目】Lab5: NetworkInterface
- 辅助脚本开发之旅
- CentOS7 安装MySQL 图文详细教程
- Directing a non-relational database introduction and deployment
- 「X」to「Earn」:赛道现状与破局思路
- 浅读一下dotenv的主干逻辑的源码
- 基于两级分解和长短时记忆网络的短期风速多步组合预测模型
- Week 7 CNN Architectures - LeNet-5、AlexNet、VGGNet、GoogLeNet、ResNet
猜你喜欢
VMware workstation program starts slowly
No-code development platform form styling steps introductory course
工业元宇宙的价值和发展
CKAN教程之在 AWS 上部署 CKAN 应用程序
数据库主键一定要自增吗?有哪些场景不建议自增?
Pytest配置项-pytest.ini
APT level comprehensive free kill with Shell
CKAN教程之将 Snowflake 连接到 CKAN 以发布到开放数据门户
The latest real software test interview questions are shared. Are you afraid that you will not be able to enter the big factory after collecting them?
resubmit 渐进式防重复提交框架简介
随机推荐
21天学习挑战赛(1)设备树的由来
CentOS7 安装MySQL 图文详细教程
centos7安装mysql8
Based on two levels of decomposition and the length of the memory network multi-step combined forecasting model of short-term wind speed
markdown语法
The latest real software test interview questions are shared. Are you afraid that you will not be able to enter the big factory after collecting them?
No code development platform data ID introductory tutorial
CKAN教程之在 AWS 上部署 CKAN 应用程序
WAF WebShell Trojan free to kill
today‘s task
PHP实现登录失败三次需要输入验证码需求
Yocto系列讲解[实战篇]85 - 制作ubi镜像和自动挂载ubifs文件系统
我们来浅谈代码语言的魅力
基于飞腾平台的嵌入式解决方案案例集 1.0 正式发布!
d实验新异常
vant-swipe自适应图片高度+图片预览
数据库审计 - 网络安全的重要组成部分
mysql根据多字段分组——group by带两个或多个参数
Week 7 - Distributional Representations
Swift中的类型相关内容