Skip to content

Instantly share code, notes, and snippets.

@hansipete
Forked from himenlinamarbric/spotifyHijack.scpt
Last active March 26, 2020 11:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hansipete/0cc51028d6c49345a2eb0e5fcac47434 to your computer and use it in GitHub Desktop.
Save hansipete/0cc51028d6c49345a2eb0e5fcac47434 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/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
@hansipete
Copy link
Author

Updated to use Apple Lossless and download and include the cover artwork in the ID3.

Screen Shot 2020-03-15 at 16 31 35

@hansipete
Copy link
Author

had little blobs of audio at the very beginning of some files. added safety measures (volume 0 and back to 100) plus 1 second delay.

@hansipete
Copy link
Author

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.

Screen Shot 2020-03-26 at 12 57 56

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