Skip to content

Instantly share code, notes, and snippets.

@himenlinamarbric
Last active September 20, 2022 18:58
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save himenlinamarbric/106274984ea7e6fd55dd to your computer and use it in GitHub Desktop.
Save himenlinamarbric/106274984ea7e6fd55dd to your computer and use it in GitHub Desktop.
Audio Hijack Pro & Spotify -- No Polling
-- 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/USERNAME/Music/rec"
property atomicparsley_path : "/usr/local/bin/atomicparsley"
-- 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_album & " - " & track_number & " - " & 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: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."
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
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 my sp_is_ad(track_id) then
set sound volume to 0
play
delay track_length
else
set player position to 0
set sound volume to 100
my ah_start_recording(spotify_session, track_counter)
play
delay track_length
my ah_stop_recording(spotify_session)
my save_metadata(metadata, track_counter)
end if
set track_id to my sp_next_track(recorded_ids, track_id)
if not my sp_is_ad(track_id) then
set recorded_ids to recorded_ids & track_id
end if
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
return {md_artist:track_artist, md_album:track_album, md_name:track_name, md_number:track_number}
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 new_filename to my format_filename(track_artist, track_album, 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)
tell current application to do shell script atomicparsley_path & " " & new_file & " --artist \"" & track_artist & "\" --album \"" & track_album & "\" --title \"" & track_name & "\" --tracknum " & track_number & " --overWrite"
end tell
end save_metadata
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
@Kovah
Copy link

Kovah commented Apr 19, 2017

Not working for me. Get the following error: "Expected class name but found identifier" and session is highlighted in the following lines

try
    set spotify_session to first session whose name is "Spotify"
on error number -1719

@funkycrash
Copy link

@Kovah just make sure you're u sing Hijack Audio 2 and not 3.
Create a "Spotify" session in Audio Hijack and start Hijacking before running the script. It should work like that.

@himenlinamarbric
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment