当前位置:网站首页>DownMusic总结记录
DownMusic总结记录
2022-08-02 22:46: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()
# 开始解析QQ音乐的搜索结果
res = jsons['music.search.SearchCgiService']['data']
list = res['body']['song']['list']
meta = res['meta']
# 数据清洗,去掉搜索结果中多余的数据
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: 搜索出来的歌曲列表
# {
# size 搜索结果总数
# next 下一搜索页码 -1表示搜索结果已经到底
# cur 当前搜索结果页码
# }
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"本地文件尺寸不符: {
os.path.getsize(localFile)}/{
int(it['size'])},开始覆盖下载 [{
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:
# 开始第二次解析
link = parseSectionByNotFound(file, it['songmid'])
else:
print(f"解析歌曲下载地址失败!{
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
# 根据文件名获取下载链接
# 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"]
# 批量下载不需要选择音质 直接开始解析为最高音质 枚举
code = ""
format = ""
qStr = ""
fsize = 0
mid = id['media_mid']
if int(id['size_hires']) != 0:
# 高解析无损音质
code = "RS01"
format = "flac"
qStr = "高解析无损 Hi-Res"
fsize = int(id['size_hires'])
elif int(id['size_flac']) != 0:
isEnc = False # 这句代码是逆向出来的 暂时无效
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 # 这句代码是逆向出来的 暂时无效
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 # 这句代码是逆向出来的 暂时无效
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 = "未分类专辑"
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']}条搜索结果.\n下一页输入n\n上一页输入p\n一键下载本页所有歌曲输入a\n若要下载某一首,请输入歌曲前方的序号。\n修改搜索关键词输入s\n请输入:", end='')
inputKey = input()
if inputKey == "n":
break
elif inputKey == "s":
print('\033c', end='')
print("请输入新的搜索关键词:", end='')
_main(input())
return
elif inputKey == 'a':
# 下载本页所有歌曲
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()
# 下载的文件要保存到哪里
# /music/就是你自定义的文件夹名称 随便指定 会自动创建
download_home = "./"
# 多线程下载 线程数量
dualThread = 16
_main()
边栏推荐
- mysql查询表中重复记录
- 了解 NFT 质押:Web3 中赚取被动收益的另一种方式
- 函数:计算组合数
- 华为设备配置BFD与接口联动(触发与BFD联动的接口物理状态变为Down)
- PHP实现登录失败三次需要输入验证码需求
- vant-swipe自适应图片高度+图片预览
- 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?
- 无代码开发平台数据ID入门教程
- IDO预售代币合约系统开发技术说明及源码分析
- Ruoyi integrates minio to realize distributed file storage
猜你喜欢

Directing a non-relational database introduction and deployment

「X」to「Earn」:赛道现状与破局思路

Test | ali internship 90 days in life: from the perspective of interns, talk about personal growth

Cholesterol-PEG-Amine,CLS-PEG-NH2,胆固醇-聚乙二醇-氨基脂两亲性脂质衍生物

第十章 时序与延迟

B站回应“HR 称核心用户都是 Loser”:该面试官去年底已被劝退,会吸取教训加强管理

centos7安装mysql5.7

2022暑假牛客多校1 (A/G/D/I)

在软件测试行业近20年的我,再来和大家谈谈今日的软件测试
![[论文总结] 深度学习在农业领域应用论文笔记10](/img/e8/0ba741980495cd81ca30bf269d1111.jpg)
[论文总结] 深度学习在农业领域应用论文笔记10
随机推荐
「X」to「Earn」:赛道现状与破局思路
科研用Cholesterol-PEG-NHS,NHS-PEG-CLS,胆固醇-聚乙二醇-活性酯
B站回应HR称用户是Loser:涉事面试官去年底已被劝退
Yocto系列讲解[实战篇]85 - 制作ubi镜像和自动挂载ubifs文件系统
Week 7 CNN Architectures - LeNet-5、AlexNet、VGGNet、GoogLeNet、ResNet
记一次mysql查询慢的优化历程
airflow db init 报错
The CTF command execution subject their thinking
MDL 内存描述符链表
【UE5 骨骼动画】全形体IK导致Two Bone IK只能斜着移动,不能平移
聚乙二醇衍生物4-Arm PEG-DSPE,四臂-聚乙二醇-磷脂
CKAN教程之在 AWS 上部署 CKAN 应用程序
Pytest配置项-pytest.ini
Unity WallFxPack使用
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?
微信小程序(一)
数据库主键一定要自增吗?有哪些场景不建议自增?
CAS:474922-22-0,DSPE-PEG-MAL,磷脂-聚乙二醇-马来酰亚胺科研试剂供应
today‘s task
程序员如何优雅地解决线上问题?