diff --git a/spotigrab.py b/spotigrab.py index 12b7ab1..8a5f0c7 100644 --- a/spotigrab.py +++ b/spotigrab.py @@ -9,19 +9,48 @@ import logging import string import unicodedata import sys +import os # configuration logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s' ) -rec_path=".\\rec\\" -loopback_dev="3" -monitor_interval=.2 + +rec_path = os.path.join('.', 'rec') +fmedia_path = os.path.join('fmedia', 'fmedia.exe') +window_monitor_interval = .2 + +window_regex = '^Spotify .+' +metadata_regex = '^Spotify - (?P.+) · (?P<artist>.+)' + # end configuration valid_filename_chars = "-_.() %s%s" % (string.ascii_letters, string.digits) -counter=0 +counter = 0 +is_recording = False + + +def get_default_device(): + device_list = subprocess.run([ + fmedia_path, + '--list-dev' + ], + capture_output=True + ) + + playback_devices = device_list.stdout.decode('utf-8') + playback_devices = playback_devices.split('Loopback:')[1].split('Capture:')[0] + playback_devices = playback_devices.split('\n') + + for device in playback_devices: + if re.search('- Default$', device): + logging.debug('found default output device: {device}'.format(device=device)) + device_number = re.match('^device #([0-9]+)', device).group(1) + break + + return device_number + def clean_filename(filename, whitelist=valid_filename_chars): # keep only valid ascii chars @@ -31,35 +60,45 @@ def clean_filename(filename, whitelist=valid_filename_chars): cleaned_filename = ''.join(c for c in cleaned_filename if c in whitelist) return cleaned_filename -def start_rec(artist, title): + +def start_rec(artist: str, title: str): def rec_subprocess(): - filename = '{rec_path}{nn:03d}. {artist} - {title}.mp3'.format( - nn=counter, - rec_path=rec_path, - artist=clean_filename(artist), - title=clean_filename(title) - ) - logging.debug('recording to {filename}'.format(filename=filename)) - command = 'fmedia --record --dev-loopback={device} -o "{filename}" --mpeg-quality=2 --globcmd=listen'.format(device=loopback_dev, filename=filename) - logging.debug('starting recording subprocess: {command}'.format(command=command)) - subprocess.run(command, shell=True, capture_output=True, check=True) + filename = os.path.join( + rec_path, + f'{counter:03d}. {clean_filename(artist)} - {clean_filename(title)}.mp3' + ) + logging.debug(f'recording to {filename}') + + command = [ + fmedia_path, + '--record', + f'--dev-loopback={loopback_dev}', + f'--out={filename}', + '--mpeg-quality=2', + f'--meta=artist={artist};title={title};tracknumber={counter}', + '--globcmd=listen' + ] + + logging.debug(f'starting recording subprocess: {" ".join(command)}') + + subprocess.run(command, check=True, capture_output=True) + + global counter + global is_recording + counter += 1 + is_recording = True logging.debug('starting subprocess') - global counter - counter += 1 thread = threading.Thread(target=rec_subprocess) thread.start() + def stop_rec(): - subprocess.run('fmedia --globcmd=stop', shell=True) + subprocess.run([fmedia_path, '--globcmd=stop']) + global is_recording + is_recording = False logging.debug('stopped recording') -def parse_win_title(win_title): - metadata = win_title.split('Spotify - ')[1].split(' · ') - title = metadata[0] - artist = metadata[1] - logging.debug('got metadata {title} by {artist}'.format(title=title, artist=artist)) - return title, artist def watch_window(): old_title = '' @@ -68,28 +107,45 @@ def watch_window(): while True: win_titles = pygetwindow.getAllTitles() for win_title in win_titles: - if re.search('^Spotify - .+', win_title): + if re.search(window_regex, win_title): if win_title != old_title: - logging.debug('window title changed to {wintitle}'.format(wintitle=win_title)) + logging.debug(f'window title changed to {win_title}') - # read metadata from window title - title, artist = parse_win_title(win_title) - - if old_title != '': - logging.debug('stop recording') + if is_recording: stop_rec() - - logging.debug('start recording now') - start_rec(title, artist) - - old_title = win_title - time.sleep(.5) -input('press enter to arm') + # if changed title matches the metadata regex it is assumed there is a song playing + if re.match(metadata_regex, win_title): + # read metadata from window title + metadata = re.match(metadata_regex, win_title) + title = metadata.group('title') + artist = metadata.group('artist') + logging.debug(f'got metadata ({title}) by ({artist})') + + logging.debug('start recording now') + start_rec(title=title, artist=artist) + + old_title = win_title + + time.sleep(window_monitor_interval) + + try: + loopback_dev = get_default_device() + + number_input = input('enter number to start counting filenames from (or press enter for 1): ') + + if number_input != '': + try: + counter = int(number_input) - 1 + except ValueError: + logging.warning(f'input "{number_input}" is a invalid number input') + watch_window() + except KeyboardInterrupt: logging.info('got CTRL+C - trying to clean up my mess') - stop_rec() + if is_recording: + stop_rec() logging.info('Bye Bye') sys.exit(0)