Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Spotify Linux mpris:artUrl patcher
#!/usr/bin/env python3
# Spotify Linux mpris:artUrl patcher
# by Giacomo Furlan
# spotify-mpris-art-fix.py
import argparse
import os
import re
import shutil
import sys
from typing import List, Optional
defaultBasePath='/usr/share/spotify-client/spotify'
parser = argparse.ArgumentParser(usage='''
\tSpotify MPRIS artUrl patcher.
\tReplaces "https://open.spotify.com/image/%%s" with "https://i.scdn.co/image/%%s".
\tExecute as administrator if needed.
''')
parser.add_argument('--bin-file', help=f'Path to the Spotify binary file (default: {defaultBasePath})', default=defaultBasePath)
parser.add_argument('--restore', action='store_true', help='Restore the last backup (and exit)')
parser.add_argument('--patch', action='store_true', help='Patch the mpris:artUrl base URL')
parser.add_argument('--no-backup', action='store_true', help='Do not make a backup of the binary (implicit --patch)')
def lastBackupFile(args: argparse.Namespace) -> Optional[str]:
baseDir = os.path.dirname(args.bin_file)
files = os.listdir(baseDir)
backupRe = re.compile(r'spotify\.bak(\.\d{1,})?$')
backupFiles = ['{}{}{}'.format(baseDir, os.path.sep, file) for file in files if backupRe.match(file)]
backupFiles.sort(reverse=True)
return backupFiles[0] if len(backupFiles) > 0 else None
def restore(args: argparse.Namespace):
restoreFile = lastBackupFile(args)
if restoreFile is None:
print('No backup file found, will not restore')
return
print(f'Restoring backup file {restoreFile}')
shutil.copyfile(restoreFile, args.bin_file)
os.remove(restoreFile)
def backup(args: argparse.Namespace):
baseBackupName = '{}{}spotify.bak'.format(os.path.dirname(args.bin_file), os.path.sep)
lastBackup = lastBackupFile(args)
backupName = baseBackupName
backupNum = 0
if lastBackup is not None:
match = re.search(r'\.(\d+)$', lastBackup)
if match is not None:
backupNum = int(match[1])
backupName += f'.{backupNum + 1}'
shutil.copyfile(args.bin_file, backupName)
def patch(args: argparse.Namespace):
buffer: Optional[bytes] = None
with open(args.bin_file, 'rb') as fh:
buffer = fh.read()
if buffer is None:
print("Can't read spotify binary")
sys.exit(0)
oldStr = b'https://open.spotify.com/image/%s'
newStr = b'https://i.scdn.co/image/%s'
newStr += (b'\x00' * (len(oldStr) - len(newStr)))
buffer = buffer.replace(oldStr, newStr)
with open(args.bin_file, 'wb') as fh:
fh.write(buffer)
args = parser.parse_args()
if args.restore:
restore(args)
sys.exit(0)
if args.patch or args.no_backup:
if not args.no_backup:
backup(args)
patch(args)
sys.exit(0)
parser.print_help()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment