musixmatch_api.py:

import base64
import hmac
from datetime import datetime
from os import urandom
from urllib import parse
from uuid import uuid4

from utils.utils import create_requests_session


class CaptchaError(Exception):
    def __init__(self, message):
        super(CaptchaError, self).__init__(message)


class UserTokenError(Exception):
    def __init__(self, message):
        super(UserTokenError, self).__init__(message)


class Musixmatch:
    def __init__(self, exception):
        self.API_URL = 'https://apic-desktop.musixmatch.com/ws/1.1/'
        self.s = create_requests_session()
        self.exception = exception
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Musixmatch/0.19.4 Chrome/58.0.3029.110 Electron/1.7.6 Safari/537.36 '
        }
        self.user_token = None

    def sign_request(self, method, params, timestamp):
        to_hash = self.API_URL + method + '?' + parse.urlencode(params)
        # Thanks to https://github.com/aaronlpv/live-lyrics/blob/master/musixmatch.c for the desktop app hmac key
        key = ("IEJ5E8XFaH" "QvIQNfs7IC").encode()
        signature = hmac.digest(key, (to_hash + timestamp).encode(), digest='SHA1')
        return base64.urlsafe_b64encode(signature).decode()

    def get_user_token_old(self):
        currenttime = datetime.now()
        timestamp = currenttime.strftime('%Y-%m-%dT%H:%M:%SZ')
        signature_timestamp = currenttime.strftime('%Y%m%d')
        method = 'token.get'
        params = {
            'format': 'json',
            'guid': str(uuid4()),
            'timestamp': timestamp,
            'build_number': '2017091202',
            'lang': 'en-GB',
            'app_id': 'web-desktop-app-v1.0'
        }
        params['signature'] = self.sign_request(method, params, signature_timestamp)
        params['signature_protocol'] = 'sha1'

        r = self.s.get(self.API_URL + method, params=params, headers=self.headers, cookies={'AWSELB': 'unknown'})
        if r.status_code != 200:
            raise Exception(r.text)

        self.user_token = r.json()['message']['body']['user_token']
        if self.user_token == 'UpgradeOnlyUpgradeOnlyUpgradeOnlyUpgradeOnly':
            raise Exception('Musixmatch: getting token failed')
        return self.user_token

    def get_user_token(self):
        r = self.s.get(f'{self.API_URL}token.get', headers=self.headers, params={
            'user_language': 'en', 'app_id': 'web-desktop-app-v1.0'
        }, cookies={
            'AWSELB': '0', 'AWSELBCORS': '0'
        })

        r = r.json()
        if r['message']['header']['status_code'] == 401 and r['message']['header']['hint'] == 'captcha':
            raise CaptchaError('Captcha required')
        elif r['message']['header']['status_code'] != 200:
            raise self.exception(f"Error: {r['message']['header']['hint']}")

        self.user_token = r['message']['body']['user_token']
        if self.user_token == 'UpgradeOnlyUpgradeOnlyUpgradeOnlyUpgradeOnly':
            raise UserTokenError('Getting user token failed')
        return self.user_token

    def _get(self, url: str, query: dict):
        params = {
            'usertoken': self.user_token,
            'app_id': 'web-desktop-app-v1.0',
        }
        params.update(query)

        r = self.s.get(f'{self.API_URL}{url}', params=params, headers=self.headers, cookies={
            'AWSELB': '0', 'AWSELBCORS': '0'
        })
        print("Response Status Code:", r.status_code)  # Debugging line
        if r.status_code not in [200, 201, 202]:
            print("Response Text (Error):", r.text)  # Debugging line
            raise self.exception(r.text)
            print("API Response:", json.dumps(response_content, indent=2))  # Debugging line
        r = r.json()
        if r['message']['header']['status_code'] == 401 and r['message']['header']['hint'] == 'captcha':
            # throw a captcha error
            raise CaptchaError('Captcha required')
        elif r['message']['header']['status_code'] != 200:
            return None

        return r['message']['body']

    def get_search_by_track(self, track_name: str, artist_name: str, album_name: str):
        # needed for richsync?
        r = self._get('matcher.track.get', {
        'q_track': track_name,
        'q_artist': artist_name,
        'q_album': album_name,
        })
        print("Track Data:", r)
        return r['track'] if r else None


    def get_track_by_isrc(self, isrc: str):
        r = self._get('track.get', {'track_isrc': isrc})
        return r['track'] if r else None

    def get_lyrics_by_id(self, track_id: str):
        r = self._get('track.lyrics.get', {'track_id': track_id})
        return r['lyrics'] if r else None

    def get_subtitle_by_id(self, common_track_id: str):
        r = self._get('track.subtitle.get', {'commontrack_id': common_track_id})
        return r['subtitle'] if r else None

    def get_rich_sync_by_id(self, track_id: str):
        # requires track_id and not common_track_id
        r = self._get('track.richsync.get', {'track_id': track_id})
        return r['richsync'] if r else None

    # Inside musixmatch_api.py, in the Musixmatch class
    def get_album_cover_url(self, album_id):
        album_info = self._get(f'album.get', {'album_id': album_id})
        if album_info is None:
            raise Exception(f"Could not get album info for album_id {album_id}")
        return album_info['album']['album_coverart_100x100']


    def get_lyrics_by_metadata(self, track_name: str, artist_name: str, album_name: str):
        return self._get('macro.subtitles.get', {
            'q_artist': artist_name,
            'q_track': track_name,
            'q_album': album_name,
            'format': 'json',
            'namespace': 'lyrics_richsynched',
            'optional_calls': 'track.richsync'
        })['macro_calls']



mycode.py:

import acrcloud
import os
import eyed3
import requests
import json
from acrcloud.recognizer import ACRCloudRecognizer



# ACR Cloud setup
config = {
‘host’: ACR_HOST,
‘access_key’: ACR_ACCESS_KEY,
‘access_secret’: ACR_ACCESS_SECRET,
‘timeout’: 10 # seconds
}
dir(acrcloud)

# Initialize the ACRCloud recognizer
recognizer = ACRCloudRecognizer(config)

# Function to recognize the song from an audio file
def recognize_song(audio_file_path):
buffer = open(audio_file_path, ‘rb’).read()
result = recognizer.recognize_by_filebuffer(buffer, 0)
try:
result_dict = json.loads(result) # Parse the JSON string into a dictionary
return result_dict[‘metadata’][‘music’][0]
except (KeyError, IndexError, json.JSONDecodeError) as e:
print(f"Error while parsing result: {e}“)
return None

# Function to set ID3 tags
def set_id3_tags_mp3(audio_file_path, tags):
audio_file = eyed3.load(audio_file_path)
if not audio_file.tag:
audio_file.initTag()

audio_file.tag.artist = tags.get(‘artists’)[0].get(‘name’)
audio_file.tag.album = tags.get(‘album’).get(‘name’)
audio_file.tag.album_artist = tags.get(‘artists’)[0].get(‘name’)
audio_file.tag.title = tags.get(‘title’)

# Set the release year (if available)
release_date = tags.get(‘release_date’)
if release_date and len(release_date) >= 4: # Check if release_date contains at least the year
year_string = release_date[:4]
try:
year = int(year_string)
# Some versions of eyeD3 require a Date object if available
if hasattr(eyed3.id3.tag, ‘Date’):
audio_file.tag.recording_date = eyed3.id3.tag.Date(year)
else:
# Otherwise, set it as text_frame
audio_file.tag.setTextFrame(“TDRC”, year_string)
except ValueError:
print(f"Invalid date format in the tag: {release_date}”)

# Add more tags here
audio_file.tag.genre = tags.get(‘genres’)[0].get(‘name’) # Assuming there’s at least one genre
audio_file.tag.publisher = “Karthik” # Publisher tag set as ‘karthik’

# To save the copyright label:
audio_file.tag.copyright = tags.get(‘label’, ‘’)

# To save the album cover page, you would need to download the image from a source
# and then do something like this:
# with open(“path_to_cover_image.jpg”, “rb”) as album_art:
# audio_file.tag.images.set(3, album_art.read(), “image/jpeg”, u"Description")


audio_file.tag.save(version=eyed3.id3.ID3_V2_3)

audio_file.tag.save()

# Replace ‘path_to_your_audio_file.mp3’ with the actual file path of the unknown song
if name == “main”:
audio_file_path = ‘C:/Users/ILEG-i5-11/Downloads/Music/Unknown_file.mp3’
song_tags = recognize_song(audio_file_path)
if song_tags:
print(f’Song identified: {song_tags}‘)
set_id3_tags_mp3(audio_file_path, song_tags)

# Renaming the file after identifying the song and setting tags
artist_name = song_tags.get(‘artists’)[0].get(‘name’)
song_title = song_tags.get(‘title’)
if artist_name and song_title:
new_file_name = f"{artist_name} - {song_title}.mp3"
new_file_path = os.path.join(os.path.dirname(audio_file_path), new_file_name)
os.rename(audio_file_path, new_file_path)
print(f"File has been renamed to: {new_file_name}“)

else:
print(‘Could not identify the song.’)


import json
import requests
from acrcloud.recognizer import ACRCloudRecognizer
from musixmatch_api import Musixmatch, CaptchaError, UserTokenError
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC, error



# ACR Cloud configuration (update with your credentials)
config = {
‘host’: ACR_HOST,
‘access_key’: ACR_ACCESS_KEY,
‘access_secret’: ACR_ACCESS_SECRET,
‘timeout’: 10 # seconds
}

recognizer = ACRCloudRecognizer(config)

# Initialize Musixmatch API (exception handling to be added as per your implementation)
musixmatch = Musixmatch(Exception)

# Function to recognize a song using ACRCloud
def recognize_song(audio_file_path):
buffer = open(audio_file_path, ‘rb’).read()
result = recognizer.recognize_by_filebuffer(buffer, 0)
try:
result_dict = json.loads(result)
return result_dict[‘metadata’][‘music’][0]
except (KeyError, IndexError, json.JSONDecodeError) as e:
print(f"Error while parsing result: {e}”)
return None

def format_time(ts):
‘’‘Converts time in seconds to the format [mm:ss.xx]’’‘
minutes = int(ts // 60)
seconds = int(ts % 60)
hundredths = int((ts - int(ts)) * 100)
return f’[{minutes:02d}:{seconds:02d}.{hundredths:02d}]‘

def process_rich_sync_lyrics(rich_sync_lyrics_json):
‘’‘Converts Musixmatch rich sync data to LRC format’’‘
lrc_lines = []
try:
# Load the JSON string into a Python object
rich_sync_data = json.loads(rich_sync_lyrics_json)
except json.JSONDecodeError as e:
print(f"Error decoding JSON: {e}")
return None

# Iterate through each line and create formatted LRC lines
for line in rich_sync_data:
ts = format_time(line[‘ts’]) # Start time of the line
lrc_line = f’{ts}{line[“x”]}’ # Use “x” for the entire line of lyrics
lrc_lines.append(lrc_line)

# Join the formatted lines with line breaks
return ‘\n’.join(lrc_lines)


# Function to get lyrics from Musixmatch given artist name and song title
def get_lyrics_from_musicxmatch(artist_name, song_title):
try:
user_token = musixmatch.get_user_token()

track_data = musixmatch.get_search_by_track(song_title, artist_name, “”)
if track_data:
track_id = track_data[‘track_id’]
rich_sync_data = musixmatch.get_rich_sync_by_id(track_id)

# Print the JSON response for debugging
print(json.dumps(track_data, indent=2))
print(json.dumps(rich_sync_data, indent=2))

if rich_sync_data and ‘richsync_body’ in rich_sync_data:
rich_sync_lyrics_json = rich_sync_data[‘richsync_body’]
lrc_lyrics = process_rich_sync_lyrics(rich_sync_lyrics_json)
return lrc_lyrics
else:
print(“No synced lyrics found.”)
return None
else:
print(“Track not found in Musixmatch.”)
return None
except (CaptchaError, UserTokenError) as e:
print(f"Error while working with Musixmatch: {e}“)
return None

if name == “main”:
audio_file_path = ‘C:/Users/ILEG-i5-11/Downloads/Music/Unknown_file.mp3’ # Replace with actual path
lrc_file_path = ‘C:/Users/ILEG-i5-11/Downloads/Music/Unknown_file.lrc’ # Output LRC file path

# Recognize the song using ACRCloud
song_tags = recognize_song(audio_file_path)
if song_tags:
artist_name = song_tags[‘artists’][0][‘name’]
song_title = song_tags[‘title’]
print(f"Identified Song: {artist_name} - {song_title}”)

# Fetch track data using the recognized song’s title and artist name
track_data = musixmatch.get_search_by_track(song_title, artist_name)
if track_data:
track_id = track_data[‘track_id’]

# Fetch the synced lyrics using the recognized song’s track ID
rich_sync_data = musixmatch.get_rich_sync_by_id(track_id)
if rich_sync_data and ‘richsync_body’ in rich_sync_data:
rich_sync_lyrics_json = rich_sync_data[‘richsync_body’]
lrc_lyrics = process_rich_sync_lyrics(rich_sync_lyrics_json)
if lrc_lyrics:
# Write the LRC lyrics to a file
with open(lrc_file_path, ‘w’, encoding=‘utf-8’) as lrc_file:
lrc_file.write(lrc_lyrics)
print(f"Saved LRC file to: {lrc_file_path}")
else:
print(“No synced lyrics found.”)
else:
print(“Track not found in Musixmatch.”)
else:
print(“Could not identify the song.”)


use these both codes: and combine into one

parameters to include to tagging a audio file are:
Title, contributing artists, artists, album, year of released, track index number in album, genre, copyright label, composer’s name, published by Karthik.
get these by musixmatch