2025-05-27 15:46:31 +08:00

549 lines
24 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
import json
import os
import re
from pydub import AudioSegment
from pydub.playback import play
import pygame
import io
import urllib.request
# Headers for the request
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36",
"Referer": "https://y.qq.com/portal/player.html",
}
def fetch_latest_music():
"""Fetch the latest music data and return structured information."""
music_info_list = []
# url = "https://c.y.qq.com/v8/fcg-bin/fcg_v8_toplist_cp.fcg?g_tk=5381&uin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=h5&needNewCode=1&tpl=3&page=detail&type=top&topid=27&_=1519963122923"
url = "https://robotstorm.tech/cyqq/v8/fcg-bin/fcg_v8_toplist_cp.fcg?g_tk=5381&uin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=h5&needNewCode=1&tpl=3&page=detail&type=top&topid=27&_=1519963122923"
try:
response = requests.get(url, headers=headers)
response.encoding = "utf-8" # Handle Chinese characters
if response.status_code == 200:
data = response.json()
# print(data)
music_list = data["songlist"]
for music in music_list:
payplay = music["data"]["pay"]["payplay"]
if payplay == 0:
music_name = music["data"]["songname"]
singer_name = music["data"]["singer"][0]["name"]
songmid = music["data"]["songmid"]
albummid = music["data"]["albummid"]
music_info_list.append((music_name, singer_name, songmid, albummid))
print(music_info_list)
return music_info_list
else:
print("Failed to fetch music data, status code:", response.status_code)
return []
except Exception as e:
print("Error occurred:", e)
return []
def get_music_info(name, page=1, num=10):
"""Search for music information."""
music_info_list = []
# url = f'https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p={page}&n={num}&w={name}'
# url = f"https://c.y.qq.com/soso/fcgi-bin/client_search_cp?w={name}"
url = f"https://robotstorm.tech/cyqq/soso/fcgi-bin/client_search_cp?w={name}"
response = requests.get(url, headers=headers).text # Get response as text
music_json = response[9:-1] # Strip extra characters
music_data = json.loads(music_json) # Convert to dictionary
music_list = music_data["data"]["song"]["list"]
for music in music_list:
payplay = music["pay"]["payplay"]
if payplay == 0:
music_name = music["songname"]
singer_name = music["singer"][0]["name"]
songmid = music["songmid"]
albummid = music["albummid"]
music_info_list.append((music_name, singer_name, songmid, albummid))
return music_info_list
def get_purl(music_info_list):
"""Get song URLs."""
music_data = []
for music in music_info_list:
music_name = music[0]
singer_name = music[1]
songmid = music[2]
albummid = music[3]
# url = (
# 'https://u.y.qq.com/cgi-bin/musicu.fcg?data={"req":{"module":"CDN.SrfCdnDispatchServer","method":"GetCdnDispatch","param":{"guid":"8846039534","calltype":0,"userip":""}},"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"8846039534","songmid":["%s"],"songtype":[0],"uin":"1152921504784213523","loginflag":1,"platform":"20"}},"comm":{"uin":"1152921504784213523","format":"json","ct":24,"cv":0}}'
# % songmid
# )
url = (
'https://robotstorm.tech/uyqq/cgi-bin/musicu.fcg?data={"req":{"module":"CDN.SrfCdnDispatchServer","method":"GetCdnDispatch","param":{"guid":"8846039534","calltype":0,"userip":""}},"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"8846039534","songmid":["%s"],"songtype":[0],"uin":"1152921504784213523","loginflag":1,"platform":"20"}},"comm":{"uin":"1152921504784213523","format":"json","ct":24,"cv":0}}'
% songmid
)
response = requests.get(url, headers=headers).json()
purl = response["req_0"]["data"]["midurlinfo"][0]["purl"]
full_media_url = "http://dl.stream.qqmusic.qq.com/" + purl
# album_img_url = (
# "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albummid + ".jpg"
# )
album_img_url = (
"https://robotstorm.tech/ygtimg/music/photo_new/T002R180x180M000" + albummid + ".jpg"
)
# lyrics_url = f"https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid={songmid}&format=json&nobase64=1"
lyrics_url = f"https://robotstorm.tech/cyqq/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid={songmid}&format=json&nobase64=1"
music_data.append(
{
"music_name": music_name,
"singer_name": singer_name,
"full_media_url": full_media_url,
"album_img_url": album_img_url,
"lyrics_url": lyrics_url,
"songmid": songmid,
}
)
print(music_data)
return music_data
def get_lyrics(music_data):
"""Get lyrics for songs."""
for music in music_data:
lyrics_url = music["lyrics_url"]
lyrics_response = requests.get(lyrics_url, headers=headers).text
print(lyrics_response)
def save_music_mp3(music_data):
"""Download songs."""
if not os.path.exists("歌曲下载"):
os.mkdir("歌曲下载")
for music in music_data:
music_name = music["music_name"]
singer_name = music["singer_name"]
full_url = music["full_media_url"]
music_response = requests.get(full_url, headers=headers).content
with open(f"歌曲下载/{music_name}-{singer_name}.mp3", "wb") as fp:
fp.write(music_response)
print(f"[{music_name}]保存成功!")
def get_song_info(songmid, music_info_list):
"""Get song details by songmid."""
# Find the music information matching the songmid
music = next((item for item in music_info_list if item[2] == songmid), None)
if not music:
return {"error": "Song not found"}
music_name = music[0]
singer_name = music[1]
albummid = music[3]
# url = (
# 'https://u.y.qq.com/cgi-bin/musicu.fcg?data={"req":{"module":"CDN.SrfCdnDispatchServer","method":"GetCdnDispatch","param":{"guid":"8846039534","calltype":0,"userip":""}},"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"8846039534","songmid":["%s"],"songtype":[0],"uin":"1152921504784213523","loginflag":1,"platform":"20"}},"comm":{"uin":"1152921504784213523","format":"json","ct":24,"cv":0}}'
# % songmid
# )
url = (
'https://robotstorm.tech/uyqq/cgi-bin/musicu.fcg?data={"req":{"module":"CDN.SrfCdnDispatchServer","method":"GetCdnDispatch","param":{"guid":"8846039534","calltype":0,"userip":""}},"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"8846039534","songmid":["%s"],"songtype":[0],"uin":"1152921504784213523","loginflag":1,"platform":"20"}},"comm":{"uin":"1152921504784213523","format":"json","ct":24,"cv":0}}'
% songmid
)
response = requests.get(url, headers=headers).json()
purl = response["req_0"]["data"]["midurlinfo"][0]["purl"]
full_media_url = "http://dl.stream.qqmusic.qq.com/" + purl
# album_img_url = (
# "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albummid + ".jpg"
# )
album_img_url = (
"https://robotstorm.tech/ygtimg/music/photo_new/T002R180x180M000" + albummid + ".jpg"
)
# lyrics_url = f"https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid={songmid}&format=json&nobase64=1"
lyrics_url = f"https://robotstorm.tech/cyqq/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid={songmid}&format=json&nobase64=1"
song_info = {
"music_name": music_name,
"singer_name": singer_name,
"full_media_url": full_media_url,
"album_img_url": album_img_url,
"lyrics_url": lyrics_url,
"songmid": songmid,
}
return song_info
# def get_song_list(categoryID):
# url = f"https://i.y.qq.com/qzone-music/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&nosign=1&disstid={categoryID}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=GB2312&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0"
# # url = f"https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&disstid={categoryID}&format=jsonp&g_tk=5381&jsonpCallback=playlistinfoCallback&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0"
# try:
# response = requests.get(url, headers=headers)
# # response.raise_for_status()
# print(response.text)
# data = response.json()
# # print(data)
# diss_info = {
# 'dissname': data['cdlist'][0]['dissname'],
# 'logo': data['cdlist'][0]['logo']
# }
# song_list = data['cdlist'][0]['songlist']
# music_info_list = []
# for music in song_list:
# payplay = music['pay']['payplay']
# if payplay == 0:
# music_name = music['songname']
# singer_name = music['singer'][0]['name']
# songmid = music['songmid']
# albummid = music['albummid']
# music_info_list.append((music_name, singer_name, songmid, albummid))
# print(music_name)
# return diss_info, music_info_list
# except requests.RequestException as e:
# print(e)
# return {}, []
def get_song_list(categoryID):
# url = f"https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&disstid={categoryID}&format=jsonp&g_tk=5381&jsonpCallback=playlistinfoCallback&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0"
url = f"https://robotstorm.tech/cyqq/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&disstid={categoryID}&format=jsonp&g_tk=5381&jsonpCallback=playlistinfoCallback&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0"
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
# 去掉jsonp包装部分
json_str = response.text.lstrip("playlistinfoCallback(").rstrip(")")
data = json.loads(json_str)
logo_url = data["cdlist"][0]["logo"]
if logo_url.startswith("https://music-file.y.qq.com"):
logo_url = logo_url.replace("https://music-file.y.qq.com", "https://robotstorm.tech/musicfileyqq")
elif logo_url.startswith("https://qpic.y.qq.com"):
logo_url = logo_url.replace("https://qpic.y.qq.com", "https://robotstorm.tech/qpicyqq")
elif logo_url.startswith("http://qpic.y.qq.com"):
logo_url = logo_url.replace("http://qpic.y.qq.com", "https://robotstorm.tech/qpicyqq")
diss_info = {
"dissname": data["cdlist"][0]["dissname"],
"logo": logo_url,
}
song_list = data["cdlist"][0]["songlist"]
music_info_list = []
for music in song_list:
payplay = music["pay"]["payplay"]
if payplay == 0:
music_name = music["songname"]
singer_name = music["singer"][0]["name"]
songmid = music["songmid"]
albummid = music["albummid"]
music_info_list.append((music_name, singer_name, songmid, albummid))
# print(music_name)
return diss_info, music_info_list
else:
print(f"Request failed with status code {response.status_code}")
return {}, []
except requests.RequestException as e:
print(e)
return {}, []
def parse_data(data):
diss_info = {
"dissname": data["cdlist"][0]["dissname"],
"logo": data["cdlist"][0]["logo"],
}
song_list = data["cdlist"][0]["songlist"]
music_info_list = []
for music in song_list:
payplay = music["pay"]["payplay"]
if payplay == 0:
music_name = music["songname"]
singer_name = music["singer"][0]["name"]
songmid = music["songmid"]
albummid = music["albummid"]
music_info_list.append((music_name, singer_name, songmid, albummid))
return diss_info, music_info_list
# def get_song_list(categoryID):
# url2 = f"https://i.y.qq.com/qzone-music/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&nosign=1&disstid={categoryID}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=GB2312&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0"
# url1 = f"https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&disstid={categoryID}&format=jsonp&g_tk=5381&jsonpCallback=playlistinfoCallback&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0"
# try:
# # 优先尝试第一个URL
# response = requests.get(url1, headers=headers, timeout=0.5)
# response.raise_for_status()
# data = response.json()
# return parse_data(data)
# except (requests.RequestException, ValueError) as e:
# print(f"URL1请求失败尝试URL2{e}")
# try:
# response = requests.get(url2, headers=headers, timeout=0.5)
# response.raise_for_status()
# # 去掉jsonp包装部分
# json_str = response.text.lstrip("playlistinfoCallback(").rstrip(")")
# data = json.loads(json_str)
# return parse_data(data)
# except requests.RequestException as e:
# print(f"URL2请求失败{e}")
# return {}, []
# def get_song_list(categoryID):
# url = f"https://i.y.qq.com/qzone-music/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&nosign=1&disstid={categoryID}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=GB2312&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0"
# try:
# # 创建请求对象
# req = urllib.request.Request(url, headers=headers)
# # 发起请求并读取响应
# with urllib.request.urlopen(req) as response:
# data = response.read().decode('utf-8')
# data = json.loads(data)
# diss_info = {
# 'dissname': data['cdlist'][0]['dissname'],
# 'logo': data['cdlist'][0]['logo']
# }
# song_list = data['cdlist'][0]['songlist']
# music_info_list = []
# for music in song_list:
# payplay = music['pay']['payplay']
# if payplay == 0:
# music_name = music['songname']
# singer_name = music['singer'][0]['name']
# songmid = music['songmid']
# albummid = music['albummid']
# music_info_list.append((music_name, singer_name, songmid, albummid))
# print(music_name)
# return diss_info, music_info_list
# except urllib.error.URLError as e:
# print(e)
# return {}, []
def fetch_album_image(singer_name):
# Constructing the search query URL
# url = f"http://music.163.com/api/search/get/web?s={singer_name}&type=100"
url = f"https://robotstorm.tech/api/search/get/web?s={singer_name}&type=100"
# Sending request with User-Agent header
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
}
try:
response = requests.get(url, headers=headers)
data = response.json()
# Extracting album image URL from the response
if data["result"]["artists"]:
artist_info = data["result"]["artists"][0]
# pic_url = artist_info.get('picUrl')
pic_url = artist_info.get("img1v1Url")
if pic_url:
return pic_url, None # Return the album image URL
else:
return None, "No album image found"
else:
return None, "Artist not found"
except Exception as e:
return None, str(e)
class MusicPlayer:
def __init__(self):
pygame.init()
pygame.mixer.init()
pygame.mixer.music.set_volume(1)
self.is_playing = False
self.is_paused = False
self.music_length = 0
self.start_position = 0
def play_music(self, url):
try:
response = requests.get(url)
if response.status_code != 200:
print(f"Failed to fetch music: HTTP {response.status_code}")
return
audio_stream = io.BytesIO(response.content)
audio = AudioSegment.from_file(audio_stream, format="m4a")
audio.export("../tmp/temp_audio.wav", format="wav")
if self.is_playing:
pygame.mixer.music.stop()
self.start_position = 0
pygame.mixer.music.load("../tmp/temp_audio.wav")
self.music_length = len(audio) / 1000 # 获取音频长度(秒)
pygame.mixer.music.set_volume(1)
print(f"Current volume: {pygame.mixer.music.get_volume()}")
pygame.mixer.music.play()
self.is_playing = True
self.is_paused = False
except Exception as e:
print(f"An error occurred while playing music: {e}")
def pause_music(self):
if self.is_playing and not self.is_paused:
pygame.mixer.music.pause()
self.is_paused = True
def resume_music(self):
if self.is_playing and self.is_paused:
pygame.mixer.music.unpause()
self.is_paused = False
def stop_music(self):
if self.is_playing:
pygame.mixer.music.stop()
self.is_playing = False
self.is_paused = False
def seek_music(self, position):
if self.is_playing:
self.start_position = position
pygame.mixer.music.load("../tmp/temp_audio.wav")
pygame.mixer.music.play(start=position)
self.is_paused = False
def get_music_length(self):
if self.is_playing:
return self.music_length
return 0
def get_current_position(self):
if self.is_playing:
# print(pygame.mixer.music.get_pos())
current_position = pygame.mixer.music.get_pos()
if current_position < 0:
self.start_position = 0
return (
current_position / 1000 + self.start_position
) # 返回当前播放位置(秒)
return 0
def fetch_music_url(self, music_page):
url = "https://music.liuzhijin.cn/"
headers = {
"Accept": "application/json, text/javascript, */*; q=0.01",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
}
data = {"input": music_page, "filter": "url", "type": "_", "page": 1}
response = requests.post(url, headers=headers, data=data)
response_data = response.json()
print(f"music_page: {music_page}, response_data: {response_data}")
# 检查是否有有效数据或错误
if response_data["code"] == 200 and response_data["data"]:
print(f"Fetched URL: {response_data['data'][0]['url']}")
return response_data["data"][0]["url"]
else:
print(f"Error fetching URL: {response_data.get('error', 'Unknown error')}")
return None
def fetch_music_url_by_mid(self, song_mid):
# 生成请求URL
# url = f"https://i.y.qq.com/v8/playsong.html?ADTAG=ryqq.songDetail&songmid={song_mid}&songid=0&songtype=0"
# url = f"http://8.138.8.114/qqmusic/v8/playsong.html?ADTAG=ryqq.songDetail&songmid={song_mid}&songid=0&songtype=0"
url = f"https://robotstorm.tech/iyqq/v8/playsong.html?ADTAG=ryqq.songDetail&songmid={song_mid}&songid=0&songtype=0"
print(url)
# 发送请求
response = requests.get(url)
# 检查请求是否成功
if response.status_code == 200:
html_str = response.text
# 正则表达式匹配获取JSON数据
match = re.search(r">window.__ssrFirstPageData__ =(.*?)<\/script", html_str)
if match:
json_str = match.group(1)
json_data = json.loads(json_str)
# 确保'songList'存在并包含至少一首歌
if "songList" in json_data and len(json_data["songList"]) > 0:
# 获取第一首歌的URL
song_data = json_data["songList"][0]
if "url" in song_data:
original_url = song_data["url"]
modified_url = original_url.replace("http://aqqmusic.tc.qq.com/", "https://robotstorm.tech/aqqmusictcqq/")
print(f"original_url:{original_url}\nmodified_url:{modified_url}")
return modified_url
else:
return "URL not found in the response data."
else:
return "Song list is empty or not found."
else:
return "No match found in HTML response."
else:
return f"Request failed with status code: {response.status_code}"
# 使用示例
player = MusicPlayer()
# songmid = "002B2EAA3brD5b"
# music_page = f"https://y.qq.com/n/yqq/song/{songmid}.html"
# music_url = player.fetch_music_url(music_page)
# player.play_music(music_url)
if __name__ == "__main__":
# latest_music = fetch_latest_music()
# if latest_music:
# print(latest_music)
# music_info_list = get_music_info("zjl")
# print(music_info_list)
# music_data = get_purl(music_info_list)
# music_data = get_purl(latest_music)
# get_lyrics(music_data)
# save_music_mp3(music_data)
# songmid = input("输入songmid:")
# song_info = get_song_info(songmid, latest_music)
# print(song_info)
# latest_music = fetch_latest_music()
# print(latest_music)
# play_song("0039MnYb0qxYhV",music_info_list)
# print(get_mp3_data("0039MnYb0qxYhV"))
# music_data = get_purl(latest_music)
# print(music_data)
# get_song_list("7299191148")
# categoryID = "9126599100" # 请替换为实际的歌单 ID
# diss_info, music_info = get_song_list(categoryID)
# print("歌单信息:", diss_info)
# print("歌曲列表:", music_info)
# print(get_song_list("7299191148"))
# while True:
# pass
# def get_mp3_data(song_mid):
# url = f"https://i.y.qq.com/v8/playsong.html?ADTAG=ryqq.songDetail&songmid={song_mid}&songid=0&songtype=0"
# print(url)
# response = requests.get(url)
# if response.status_code == 200:
# html_str = response.text
# match = re.search(r">window.__ssrFirstPageData__ =(.*?)<\/script", html_str)
# if match:
# json_str = match.group(1)
# json_data = json.loads(json_str)
# return json_data
# else:
# print("No match found in HTML response.")
# return None
# else:
# print(f"Request failed with status code: {response.status_code}")
# return None
print(player.fetch_music_url_by_mid("004g2ZZ64a5kAN"))
print(player.fetch_music_url("https://y.qq.com/n/yqq/song/004g2ZZ64a5kAN.html"))