Skip to content

Instantly share code, notes, and snippets.

@imjyotiraditya
Created May 18, 2025 11:26
Show Gist options
  • Save imjyotiraditya/7146b47c09e1227b3c6adcee4d4b4f0e to your computer and use it in GitHub Desktop.
Save imjyotiraditya/7146b47c09e1227b3c6adcee4d4b4f0e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Jyotiraditya Panda <[email protected]>
# SPDX-License-Identifier: MIT
import argparse
import sys
import requests
BASE_URL = "https://zvuk.com"
API_ENDPOINTS = {
"lyrics": f"{BASE_URL}/api/tiny/lyrics",
"stream": f"{BASE_URL}/api/tiny/track/stream",
"graphql": f"{BASE_URL}/api/v1/graphql",
"profile": f"{BASE_URL}/api/tiny/profile",
}
TOKEN = ""
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Content-Type": "application/json",
}
def get_anonymous_token():
try:
response = requests.get(API_ENDPOINTS["profile"], headers=HEADERS)
response.raise_for_status()
data = response.json()
if "result" in data and "token" in data["result"]:
return data["result"]["token"]
raise ValueError("Token not found in API response")
except Exception as e:
raise Exception(f"Failed to retrieve anonymous token: {e}")
def get_auth_cookies():
global TOKEN
if not TOKEN:
TOKEN = get_anonymous_token()
return {"auth": TOKEN}
def get_track_lyrics(track_id):
params = {"track_id": track_id}
response = requests.get(
API_ENDPOINTS["lyrics"],
params=params,
headers=HEADERS,
cookies=get_auth_cookies(),
)
response.raise_for_status()
data = response.json()
if "result" in data and "lyrics" in data["result"]:
return data["result"]["lyrics"]
return "No lyrics found"
def get_stream_url(track_id):
params = {"id": track_id, "quality": "mid"}
response = requests.get(
API_ENDPOINTS["stream"],
params=params,
headers=HEADERS,
cookies=get_auth_cookies(),
)
response.raise_for_status()
data = response.json()
if "result" in data and "stream" in data["result"]:
return data["result"]["stream"]
raise ValueError("No stream URL found in API response")
def search_tracks(query):
graphql_query = """
query getSearchTracks($query: String) {
search(query: $query) {
tracks(limit: 50) {
items {
id
title
duration
explicit
artists {
id
title
}
release {
id
title
}
}
}
}
}
"""
payload = {
"query": graphql_query,
"variables": {"query": query},
"operationName": "getSearchTracks",
}
response = requests.post(
API_ENDPOINTS["graphql"],
json=payload,
headers=HEADERS,
cookies=get_auth_cookies(),
)
response.raise_for_status()
data = response.json()
if (
"data" in data
and "search" in data["data"]
and "tracks" in data["data"]["search"]
):
return data["data"]["search"]["tracks"]["items"]
return []
def format_duration(seconds):
minutes, seconds = divmod(seconds, 60)
return f"{minutes}:{seconds:02d}"
def stream_command(args):
try:
if not TOKEN:
sys.stderr.write(
"Error: Authentication token must be manually set for stream functionality.\n"
)
sys.stderr.write("To get a token:\n")
sys.stderr.write("1. Log in to Zvuk.com in your browser\n")
sys.stderr.write("2. Visit https://zvuk.com/api/v2/tiny/profile\n")
sys.stderr.write("3. Copy the token value from the response\n")
sys.stderr.write("4. Set the TOKEN variable in this script\n")
sys.exit(1)
stream_url = get_stream_url(args.track_id)
print(stream_url)
except Exception as e:
sys.stderr.write(f"Error: {e}\n")
sys.exit(1)
def lyrics_command(args):
try:
lyrics = get_track_lyrics(args.track_id)
print(lyrics)
except Exception as e:
sys.stderr.write(f"Error: {e}\n")
sys.exit(1)
def search_command(args):
try:
tracks = search_tracks(args.query)
if not tracks:
print("No tracks found")
return
print(f"Found {len(tracks)} tracks:")
for i, track in enumerate(tracks, 1):
artists = ", ".join([artist["title"] for artist in track["artists"]])
duration = format_duration(track["duration"])
print(f"{i}. {track['title']} - {artists} ({duration}) [ID: {track['id']}]")
except Exception as e:
sys.stderr.write(f"Error: {e}\n")
sys.exit(1)
def main():
parser = argparse.ArgumentParser(
description="Zvuk.com API client for searching tracks and fetching lyrics and streams"
)
subparsers = parser.add_subparsers(dest="command", help="Command to run")
stream_parser = subparsers.add_parser("stream", help="Get stream URL for a track")
stream_parser.add_argument("track_id", help="Track ID to fetch stream for")
stream_parser.set_defaults(func=stream_command)
lyrics_parser = subparsers.add_parser("lyrics", help="Get lyrics for a track")
lyrics_parser.add_argument("track_id", help="Track ID to fetch lyrics for")
lyrics_parser.set_defaults(func=lyrics_command)
search_parser = subparsers.add_parser("search", help="Search for tracks")
search_parser.add_argument("query", help="Search query")
search_parser.set_defaults(func=search_command)
args = parser.parse_args()
if hasattr(args, "func"):
args.func(args)
else:
parser.print_help()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment