Skip to content

Commit

Permalink
Huge update! More in the description.
Browse files Browse the repository at this point in the history
– Hopefuly fixing issue #5 (crash on Spotify updates)
– Improved general stability
– Added optional logging functionality
– General code cleanup
– Removed some redundant comments
  • Loading branch information
artemgordinskiy committed Feb 16, 2013
1 parent f56d8fd commit 3e19eaf
Showing 1 changed file with 102 additions and 52 deletions.
154 changes: 102 additions & 52 deletions SpotiFree.applescript
@@ -1,19 +1,13 @@
-- Setting the "playing" property to the constant returned by Spotify as a "player state" when it's playing.
property playing : Çconstant ****kPSPÈ
global currentVolume
property debug : false
property currentVolume : null

-- Check if Spotifree is being run the first time and is in Login Items.
if (my isTheFirstRun() = true and my isInLoginItems() = false) then
-- Tell the user how Spotifree runs and ask if he wants Spotifree to run automatically on startup.
-- Assign a result to the variable 'userAnswer'.
set userAnswer to the button returned of (display dialog "Hi, thanks for installing Spotifree!" & Â
return & "Just so you know, Spotifree has no interface yet, and will work silently in the background." & return & return Â
& "BTW, do you want it to run automatically on startup?" with title Â
"You are awesome!" with icon 1 buttons {"No, thanks", "OK"} default button 2)
-- Check if user agreed.
if (userAnswer = "OK") then
try
-- Add Spotifree to the Login Items.
my addToLoginItems()
end try
end if
Expand All @@ -25,45 +19,47 @@ end if

repeat
try
-- Checking if Spotify is running, playing, and if current track is an advertisement.
if (isRunning() and isPlaying() and isAnAd()) then
try
-- Mute Spotify.
mute()
end try
repeat
try
-- Check if current track is an ad. Or if Spotify was paused during an advertisement.
if (isAnAd()) then
-- If Spotify was paused, or there is a second ad, this loop will continue to repeat.
else
-- If there's no more ads, pause for .5 seconds to let Spotify respond.
-- Then, unmute and exit the loop.
delay 0.5
if (isAnAd() = false) then
-- Have to delay a little bit, because Spotify may tell us about the next track too early,
-- and a user has to hear the last half-second of an ad.
delay 0.8
unmute()
exit repeat
end if
end try
delay 0.3
end repeat
else
-- Delaying, to get less of the crashing "connection is invalid" errors.
delay 1
end if
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "some unhandled error in the outer loop.")
end try
-- This is how fast we are polling Spotify.
-- The only speed at which you will hear no sounds passing through.
-- Fortunately, combined with the added load on Spotify, the CPU usage stays well below 1% even on an old dual-core 3.6 GHz processor.
-- The only speed at which you will hear almost no sounds passing through.
-- Fortunately, even combined with the added load on Spotify, the CPU usage is rather miniscule.
delay 0.3
end repeat

on mute()
try
tell application "Spotify"
-- Get the current sound volume from Spotify and save it in a variable currentVolume.
set currentVolume to sound volume
-- This is the only way possible to mute Spotify during an advertisement. Otherwise it pauses when you mute the sound.
-- This is the only way possible to mute Spotify during an advertisement.
-- Otherwise it pauses when you mute the sound.
pause
set sound volume to 0
play
end tell
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "trying to mute Spotify.")
end try
return
end mute
Expand All @@ -74,24 +70,26 @@ on unmute()
-- Restore the volume to the level supplied to the parameter.
set sound volume to currentVolume
end tell
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "trying to restore Spotify's volume.")
end try
return
end unmute

on isAnAd()
local currentTrackPopularity, currentTrackDuration
set currentTrackPopularity to null
set currentTrackDuration to null
try
tell application "Spotify"
-- Get the popularity of a current track and save it in a variable currentTrackPopularity.
set currentTrackPopularity to popularity of current track
-- Get the duration of current track and save it in a variable currentTrackDuration.
set currentTrackDuration to duration of current track
end tell
on error
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "trying to get popularity and duration of the current track.")
return false
end try
-- If current track's popularity is 0 and its duration is 40 seconds or less, then it's almost certainly an ad.
if (currentTrackPopularity = 0 and currentTrackDuration ² 40) then
if ((currentTrackPopularity ­ null and currentTrackDuration ­ null) and (currentTrackPopularity = 0 and currentTrackDuration ² 40)) then
return true
else
return false
Expand All @@ -100,16 +98,19 @@ end isAnAd

on isPlaying()
local playerState
try
tell application "Spotify"
-- Hook the variable playerState to Spotify's state (playing, paused etc.).
set playerState to player state
end tell
on error
return false
end try
-- Compare Spotify's state with a constant saved in the property on line 1.
if playerState = playing then
set playerState to null
-- Adding a 5 seconds timeout instead of default 2 min. Prevents long unresponsiveness of the app.
with timeout of (5) seconds
try
tell application "Spotify"
set playerState to player state
end tell
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "counting Spotify processes.")
return false
end try
end timeout
if (playerState ­ null and playerState = Çconstant ****kPSPÈ) then
return true
else
return false
Expand All @@ -118,16 +119,18 @@ end isPlaying

on isRunning()
local spotifyProcesses
try
tell application "System Events"
-- Check if there are any Spotify processes. Set to variable spotifyProcesses
set spotifyProcesses to (count of (every process whose bundle identifier is "com.spotify.client"))
end tell
on error
return false
end try
-- Check the variable spotifyProcesses, to see is Spotify running.
if spotifyProcesses ­ 0 then
-- Adding a 5 seconds timeout instead of default 2 min. Prevents long unresponsiveness of the app.
with timeout of (5) seconds
try
tell application "System Events"
set spotifyProcesses to (count of (every process whose name is "Spotify"))
end tell
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "counting Spotify processes.")
return false
end try
end timeout
if spotifyProcesses = 1 then
return true
else
return false
Expand All @@ -140,7 +143,8 @@ on addToLoginItems()
-- Add Spotifree to the Login Items.
make login item at end with properties {name:"Spotifree", path:"/Applications/Spotifree.app", hidden:true}
end tell
on error
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "adding Spotifree to the Login Items")
return
end try
end addToLoginItems
Expand All @@ -149,13 +153,14 @@ on isTheFirstRun()
try
-- Get the value of the key 'hasRanBefore' in the file "~/Library/Preferences/com.ArtemGordinsky.Spotifree"
set hasRanBefore to do shell script "defaults read com.ArtemGordinsky.Spotifree 'hasRanBefore'"
on error
on error errorMessage number errorNumber
-- If the file not there yet, an error is going to be thrown. So it's the first run, probably.
-- We are going to return 'true' even if it was some other error. Not a big deal, after all.
my log_error(errorNumber, errorMessage, "checking is Spotify being run the first time.")
return true
end try

if (hasRanBefore ­ "true") then
if (hasRanBefore = "true") then
return true
else
return false
Expand All @@ -164,15 +169,60 @@ end isTheFirstRun

on isInLoginItems()
try
-- Ask 'System Events' is 'Spotifree' is in 'Login Items'.
tell application "System Events"
if login item "Spotifree" exists then
return true
else
return false
end if
end tell
on error
on error errorMessage number errorNumber
my log_error(errorNumber, errorMessage, "checking if Spotify is in Login Items")
return false
end try
end isInLoginItems
end isInLoginItems

on log_message(message)
local content
set content to (return & Â
"-----------------------------------------------------------" & Â
return & my dateAndTime() & return & message & return Â
& "-----------------------------------------------------------" & return)
set log_file to (((path to desktop folder) as text) & "Spotifree_log.txt")
my write_to_file(content, log_file, true)
end log_message

on log_error(error_number, error_message, diag_message)
local content
if (debug = true) then
set content to (return & "-----------------------------------------------------------" & Â
return & my dateAndTime() & return & "Error number: " & error_number Â
& return & "Error message: " & error_message & return & Â
"Diagnostic message: " & diag_message & return Â
& "-----------------------------------------------------------" & return)
set log_file to (((path to desktop folder) as text) & "Spotifree_log.txt")
my write_to_file(content, log_file, true)
end if
end log_error

on write_to_file(this_data, target_file, append_data) -- (string, file path as string, boolean)
try
set the target_file to the target_file as text
set the open_target_file to Â
open for access file target_file with write permission
if append_data is false then Â
set eof of the open_target_file to 0
write this_data to the open_target_file starting at eof
close access the open_target_file
return true
on error
try
close access file target_file
end try
end try
end write_to_file

on dateAndTime()
set currentDateAndTime to (current date) as string
return currentDateAndTime
end dateAndTime

0 comments on commit 3e19eaf

Please sign in to comment.