-
-
Save hansipete/0cc51028d6c49345a2eb0e5fcac47434 to your computer and use it in GitHub Desktop.
Audio Hijack Pro & Spotify -- No Polling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Usage | |
-- 1. Edit the settings (see below). This step is NECESSARY the first time you use it! | |
-- 2. Run this script (this should open Audio Hijack Pro and Spotify) | |
-- | |
-- NOTE: the script assumes that each track is played entirely. You CANNOT skip tracks in Spotify. | |
-- If you do so the script will get out of sync and the resulting files contain partial or multiple songs. | |
-- You can abort a recordig session by stopping this script and ending (manually) the recording | |
-- in Audio Hijack Pro. | |
-- | |
-- You need to have "atomicparsley" installed on your system. You can install the application with | |
-- homebrew using "brew install atomicparsley" (see http://brew.sh/) | |
-- | |
-- The recording settings for Audio Hijack Pro NEED to be defined here, they CANNOT be changed | |
-- withing the application itself. | |
-- Every time the script is executed, the settings defined here are applied to Audio Hijack Pro, | |
-- customizations in the application are overwritten. | |
property output_folder : "/Users/hans/Music/rec/" | |
property atomicparsley_path : "/usr/local/bin/atomicparsley" | |
property comment : "" | |
property safety_silence_beginning : 1.0 | |
property safety_silence_end : 0.1 | |
property spotify_cmd_delay : 0.8 | |
-- In order to change the output format, you need to find the line in this script which looks like | |
-- "set recording format of spotify_session to {...}" | |
-- If you change the output format, make sure to update the file_extension property to match the codec! | |
property file_extension : ".m4a" | |
on format_filename(track_artist, track_album, track_name, track_number) | |
set r to track_artist & " - " & track_name & file_extension | |
set x to length of r | |
if (x > 250) then set x to 250 | |
return ((characters 1 thru x of r) as string) | |
end format_filename | |
tell application "Audio Hijack Pro" | |
activate | |
-- Find the "Spotify" session or create it if it doesn't exist. | |
try | |
set spotify_session to first session whose name is "Spotify" | |
on error number -1719 | |
tell application "Finder" | |
set spotify_path to POSIX path of (application file id "spty" as alias) | |
end tell | |
set spotify_session to make new application session at end of sessions | |
set name of spotify_session to "Spotify" | |
set targeted application of spotify_session to spotify_path | |
end try | |
-- Define / overwrite the settings of the "Spotify" session in Audio Hijack Pro. | |
-- This script only works correctly if the settings are as specified here. | |
set output folder of spotify_session to output_folder | |
set output name format of spotify_session to "%tag_title" | |
-- Audio format settings | |
set recording format of spotify_session to {encoding:Apple Lossless, channels:Stereo} | |
--set recording format of spotify_session to {encoding:AAC, bit rate:256, channels:Stereo} | |
-- set recording format of spotify_session to {encoding:MP3, bit rate:320, channels:Stereo, style:VBR} | |
-- (Re-)start hijacking and recording on the spotify session | |
if hijacked of spotify_session is true then | |
stop hijacking spotify_session | |
end if | |
start hijacking spotify_session relaunch yes | |
end tell | |
delay 2 | |
tell application "Spotify" | |
if not running then activate | |
if player state is playing then pause | |
if repeating then display dialog "Note that repeating is enabled in Spotify! You may want to disable it now." | |
end tell | |
tell application "Audio Hijack Pro" | |
display alert "Ready for recording. Please start playing the first song in the album / playlist, then click OK in this dialog." | |
-- little cleanup before we start | |
tell current application to do shell script ("find -E " & output_folder & " -regex './[0-9]+(.[0-9])?.(jpg|m4a)' -delete") | |
end tell | |
tell application "Spotify" | |
set track_counter to 0 | |
set recorded_ids to {} | |
set track_id to my sp_next_track(recorded_ids, "") | |
set recorded_ids to recorded_ids & track_id | |
set sound volume to 100 | |
repeat until track_id is equal to "" | |
set track_counter to (track_counter + 1) | |
set metadata to my sp_current_metadata() | |
set track_length to (duration of current track) / 1000 | |
-- if track already exists in folder, skip | |
if my is_song_already_downloaded(metadata) is not true then | |
set player position to 0 | |
delay spotify_cmd_delay | |
my ah_start_recording(spotify_session, track_counter) | |
log " start recording and add a little sec silcence to beginning of file: " & safety_silence_beginning | |
delay safety_silence_beginning | |
play | |
delay (track_length) | |
log " stop recording" | |
pause | |
delay safety_silence_end | |
my ah_stop_recording(spotify_session) | |
delay spotify_cmd_delay | |
play -- make sure playlist contonues... wierdbug sometimes. | |
log " save metadata" | |
my save_metadata(metadata, track_counter) | |
-- delay spotify_cmd_delay | |
else | |
log "Skipped song..." | |
log metadata | |
log "Next song spotify" | |
next track | |
end if -- song not yet downloaded | |
set track_id to my sp_next_track(recorded_ids, track_id) | |
set recorded_ids to recorded_ids & track_id | |
end repeat | |
end tell | |
on ah_start_recording(spotify_session, track_counter) | |
tell application "Audio Hijack Pro" | |
set title tag of spotify_session to track_counter | |
start recording spotify_session | |
end tell | |
end ah_start_recording | |
on ah_stop_recording(spotify_session) | |
tell application "Audio Hijack Pro" | |
stop recording spotify_session | |
end tell | |
end ah_stop_recording | |
on sp_is_ad(track_id) | |
return (track_id starts with "spotify:ad:") | |
end sp_is_ad | |
on sp_current_metadata() | |
tell application "Spotify" | |
try | |
set current_track to current track | |
set track_artist to artist of current_track | |
set track_album to album of current_track | |
set track_name to name of current_track | |
set track_number to track number of current_track | |
set track_artwork_url to artwork url of current_track | |
set track_id to id of current_track | |
return {md_id:track_id, md_artist:track_artist, md_album:track_album, md_name:track_name, md_number:track_number, md_artwork_url:track_artwork_url} | |
on error number -1728 -- no current track | |
return {} | |
end try | |
end tell | |
end sp_current_metadata | |
on sp_next_track(recorded_ids, current_id) | |
tell application "Spotify" | |
try | |
set next_id to (id of current track) | |
on error number -1728 -- no current track | |
set next_id to current_id | |
end try | |
-- `next track` does not work on ads | |
repeat while next_id is equal to current_id | |
delay 1 | |
try | |
set next_id to (id of current track) | |
on error number -1728 -- no current track | |
set next_id to current_id | |
end try | |
end repeat | |
pause | |
if recorded_ids contains next_id then | |
return "" | |
else | |
return next_id | |
end if | |
end tell | |
end sp_next_track | |
on save_metadata(metadata, track_counter) | |
tell application "Spotify" | |
set track_artist to md_artist of metadata | |
set track_album to md_album of metadata | |
set track_name to md_name of metadata | |
set track_number to md_number of metadata | |
set artwork_url to md_artwork_url of metadata | |
set track_id to md_id of metadata | |
-- download image form url and pass path | |
set artwork_file to (output_folder & "/" & track_counter & ".jpg") | |
tell current application to do shell script ("curl --silent " & artwork_url & " > " & artwork_file) | |
-- wait for download to complete | |
my wait_file_exists(artwork_file) | |
set new_filename to my format_filename(my deQuoteString(track_artist), my deQuoteString(track_album), my deQuoteString(track_name), track_number) | |
-- eliminate special characters from file name | |
-- TODO: keep brackets [], also other symbols | |
-- NOTE: 'do shell script' within a 'tell' block requires 'tell current application to', https://developer.apple.com/library/mac/releasenotes/AppleScript/RN-AppleScript/RN-10_6/RN-10_6.html#//apple_ref/doc/uid/TP40000982-CH106-SW6 | |
tell current application to set new_filename to do shell script ("echo \"" & new_filename & "\" | sed 's/[^a-zA-Z0-9 .äöüÄÖÜéèàç&(){}-]/_/g'") | |
my wait_file_exists(my temp_filename(track_counter, false)) | |
set current_file to my temp_filename(track_counter, true) | |
set new_file to ("\"" & output_folder & "/" & new_filename & "\"") | |
tell current application to do shell script ("mv " & current_file & " " & new_file) | |
-- update metadata (tags) & clean up jpgs | |
tell current application to do shell script (atomicparsley_path & " " & new_file & " --artist \"" & my deQuoteString(track_artist) & "\" --album \"" & my deQuoteString(track_album) & "\" --title \"" & my deQuoteString(track_name) & "\" --tracknum " & track_number & " --copyright \"" & track_id & "\" --artwork " & artwork_file & " --comment \"" & comment & "\" --overWrite && rm " & output_folder & "/*.jpg") | |
end tell | |
end save_metadata | |
on parseLine(theLine, delimiter) | |
-- This came from Nigel Garvey | |
set astid to AppleScript's text item delimiters | |
set AppleScript's text item delimiters to {delimiter} | |
set theTextItems to theLine's text items | |
set AppleScript's text item delimiters to astid | |
repeat with i from 1 to (count theTextItems) | |
if (item i of theTextItems is "") then set item i of theTextItems to missing value | |
end repeat | |
return theTextItems's every text | |
end parseLine | |
on deQuoteString(aString) | |
if aString contains "\"" then | |
set parsedString to my parseLine(aString, "\"") | |
set parsedCount to count of parsedString | |
set aString to "" | |
repeat with i from 1 to parsedCount | |
if i < parsedCount then | |
set aString to (aString & item i of parsedString as text) & "\\\"" | |
else | |
set aString to aString & item i of parsedString as text | |
end if | |
end repeat | |
end if | |
return aString | |
end deQuoteString | |
on is_song_already_downloaded(metadata) | |
tell application "Spotify" | |
set track_artist to md_artist of metadata | |
set track_album to md_album of metadata | |
set track_name to md_name of metadata | |
set track_number to md_number of metadata | |
set artwork_url to md_artwork_url of metadata | |
set new_filename to my format_filename(my deQuoteString(track_artist), my deQuoteString(track_album), my deQuoteString(track_name), track_number) | |
tell current application to set new_filename to do shell script ("echo \"" & new_filename & "\" | sed 's/[^a-zA-Z0-9 .äöüÄÖÜéèàç&(){}-]/_/g'") | |
set new_file to (output_folder & "/" & new_filename) | |
return my file_exists(new_file) | |
end tell | |
end is_song_already_downloaded | |
on temp_filename(track_counter, add_quote) | |
set filename to output_folder & "/" & track_counter & file_extension | |
if add_quote then | |
return ("\"" & filename & "\"") | |
else | |
return filename | |
end if | |
end temp_filename | |
on wait_file_exists(filename) | |
set c to 0 | |
repeat until c is equal to 10 | |
if file_exists(filename) then | |
return | |
end if | |
set c to c + 1 | |
delay 0.1 | |
end repeat | |
error "output file could not be found: " & filename | |
end wait_file_exists | |
on file_exists(filename) | |
tell application "System Events" | |
if exists file filename then | |
return true | |
else | |
return false | |
end if | |
end tell | |
end file_exists |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
had more issues with blobs of audio at the very end. now everything seems to be fine. applescript->spotify seems to have a little lag before commands are performed.
this needs some cleanup... but it's recording beautiful now.