Preview songs in Spotify with Applescript - NeedleDrop from Doug - applescript

We all know Doug's applescripts for iTunes. However, with the growth of streaming music, I don't use iTunes so much anymore, but rather Spotify.
I was trying to adapt the NeedleDrop script to preview songs. The script allows to set
a - A start from given time, f.e. start playing the songs at 30 seconds
b - Play that song for a given time, f.e. 10 seconds.
The problem is the script starts the first song at the given time and plays for the given time, but then, the next songs play for a given time, but start at the beginning instead of the given value in time.
The script is under GNU license by Doug and the license is included.
I'm posting both the script and the exported app from it. If anyone has an idea ?!
LINK: Download
Here is the script provided in the download file:
-- handler to get a number from user
to get_a_number(pmpt, addenda, defnum)
set rez to (display dialog addenda & pmpt default answer defnum buttons {"Cancel ", "OK"} default button 2 with title "Needle Drop")
if button returned of rez starts with "cancel" then tell me to quit
set myNumber to text returned of rez
try
if myNumber is "" then get_a_number(pmpt, "Enter only numbers..." & return & return, defnum)
return (myNumber as integer)
on error -- m number n
get_a_number(pmpt, "Enter only numbers..." & return & return, defnum)
end try
end get_a_number
global start_time -- seconds into each track to begin playing
global needle_drop_interval -- seconds to play each track
on run
-- get number of seconds between songs
set needle_drop_interval to my get_a_number("Play each track for how many seconds?", "", "10")
-- get seconds into each track to play
set start_time to my get_a_number("How many seconds into each track to start playing?", "", "10")
-- play first song in the playlist
tell application "Spotify"
activate
set player position to start_time
play
delay needle_drop_interval
end tell
end run
on idle
tell application "Spotify"
if player state is not playing then tell me to quit
pause
next track
set player position to start_time
play
end tell
return needle_drop_interval
end idle
on quit
try
tell application "Spotify" to stop
end try
continue quit
error number -128
end quit

The provided script works fine for offline playlists but not for streamed ones. Spotify probably doesn't have time to buffer the track or something otherwise.
When I added a delay to give spotify time to buffer it worked fine. (You can experiment with shorter and longer delays)
tell application "Spotify"
if player state is not playing then tell me to quit
next track
pause
set player position to start_time
delay 1
play
end tell
Note the row switch between 'next track' and 'pause'

Amazing !
However, after some songs, the bug still happened sometimes, which seems to get totally fixed by adding a delay before the set player position.
This could sound contradictory, but worked better for me.
Thanks !
on idle
tell application "Spotify"
if player state is not playing then tell me to quit
next track
pause
delay 1
set player position to start_time
play
end tell
return needle_drop_interval
end idle

Related

How to fix "original file could not be found" error via apple script

I have an issue with my music library.
Some songs I am unable to play because they cannot be found locally.
Here's an example of the error messages I get when playing a specific song:
The song ... could not be used because the original file could not be found. Would you like to locate it?
I can simply press Cancel and the song will be matched via the Apple Music Service.
This allows me to then play the song.
This issue has been discussed here, albeit not in an automated way. Hence, I would like to find an automated solution.
For this, I took the approach of looping through my library by playing each song.
By default, if a song cannot be found, the script automatically skips to the next song.
However, I would like the script to deal with the "file not found" errors and press Cancel.
My current attempt unfortunately does not work:
-- Play first song in library (turn off shuffle and repeat)
set i to 4000 --number of songs in library
repeat while i > 0
tell application "Music" to play (next track)
tell application "System Events"
key code 53
end tell
set i to i - 1
end repeat
How can I force the script to deal with these pop-up errors?
Note: I am also open to any other, more efficient solution to my problem if you have any suggestions. I decided not to go for the Locate option because it takes more time and I will delete any unreferenced songs from my disk at a later stage anyways.
UPDATED VERSION. Following script doesn't play tracks and programatically clicks button "Cancel" when track is corrupted. Like described by OP fixing tracks manually workflow:
tell application "Music"
activate -- required
tell source "Library"
repeat with nextTrack in tracks
try
with timeout of 2 seconds
set theResult to play (contents of nextTrack)
end timeout
theResult
on error
delay 1
tell application "System Events" to tell process "Music" to tell window 1 to if UI element "Cancel" exists then click UI element "Cancel"
end try
stop
end repeat
end tell
end tell

Detect the end of a Spotify track with Applescript?

I'm writing an Applescript that displays a notification with the name, album, and artist of the current Spotify track. Yes, I'm aware there is already an app that does this, but even with the latest version, it's very buggy and behaves erratically.
So far, I have a good little script. Here it is:
repeat until application "Spotify" is not running
tell application "Spotify"
set theSong to name of current track
set theAlbum to album of current track
set theArtist to artist of current track
set theTime to player position
set theDuration to duration of current track
end tell
(* This sets the amount of delay to the length of the song minus the current place in the song, which leaves us with the amount of time left until the next song plays.*)
set delayTime to theDuration - theTime + 0.5
tell application "Spotify"
if player state is playing then
display notification theArtist with title theSong subtitle theAlbum
end if
delay delayTime
end tell
end repeat
Basically, the problem with this is if I pause or change the track, delay stays the same and thus doesn't display the notification when the song starts.
I used delay in this script because I don't really know a better way of checking for the end of a track. Is there any Applescript event for the end of a song (or the beginning of a new one?)
Thanks.
P.S., If this question is too complicated, feel free to ask more.
Use a loop to check the current track, exit the loop when the song change
repeat until application "Spotify" is not running
tell application "Spotify"
set theSong to name of current track
set theAlbum to album of current track
set theArtist to artist of current track
set thisTrack to current track
if player state is playing then
display notification theArtist with title theSong subtitle theAlbum
repeat until thisTrack is current track
delay 3
end repeat
end if
end tell
end repeat

Get duration of the current song in AppleScript

I'm trying to make a script to get the duration of the current song, but when I do this the duration comes back as "missing value". Any suggestions?
tell application "iTunes"
if player state is playing or player state is paused then
set theDuration to (get duration of the current track)
display dialog theDuration
end if
end tell
Your code works perfectly here. For example, I play "How Many Times" from Bob Marley and when I run your script the dialog shows 145.1920013427734. But (of course) when I play Internet Radio, duration will return nothing. Try a local track and run your script again ? Just guessing. (iTunes 11.0.5 / OS X 10.8.2)

Applescript editor not responding

I did the following script in order to identify itunes songs that doesn't have artwork. It's based on other script that I found in net.
tell application "iTunes"
repeat with a in every track of playlist "Library"
if not (exists (artwork 1 of a)) then
add (get location of a) to (playlist "noart")
end if
end repeat
end tell
It seems to be working, it compiles well, and because I can that it in event log windows:
tell application "iTunes"
count every track of playlist "Library"
--> 10684
exists artwork 1 of item 1 of every track of playlist "Library"
--> true
exists artwork 1 of item 2 of every track of playlist "Library"
--> true
But after 4 hundred tracks, it starts to run slowly, and applescript stops responding after one thousand tracks.
I thought that maybe I could be exhausting my mac memory, but in Activity Monitor, I can see that Applescript is consuming 100% CPU and less than 50MB of memory. I'm running macos 10.7.4 on a macbook pro (i7 with 4GB ram).
As you can see my itunes library has 10684 tracks. It's not a small library, but it's not a huge one.
Does anyone has any advice? Or a script to identify tracks without artwork?
TIA,
Bob
Here's what I use. My main suggestion would be to use "duplicate" instead of "add" and then you do not need to get the location of a track. Also you'll see I'm using "a reference to" most things which makes it work faster. I also create the "no artwork" playlist with a time stamp on-the-fly so I can see when I ran the script.
set d to current date
set missingTracksCount to 0
tell application "iTunes"
set isFixedIndexing to fixed indexing
if not isFixedIndexing then set fixed indexing to true
-- make a new playlist to hold the tracks
set newPlaylist to make new playlist
set name of newPlaylist to "No Art - " & month of d & " " & day of d & " " & time string of d
set mainPlaylist to a reference to playlist "Library"
set noArtworkPlaylist to a reference to newPlaylist
set trackCount to count of tracks of mainPlaylist
repeat with i from 1 to trackCount
set trackRef to (a reference to (track i of mainPlaylist))
if (count of artworks of trackRef) is less than 1 then
duplicate trackRef to noArtworkPlaylist
set missingTracksCount to missingTracksCount + 1
end if
end repeat
if not isFixedIndexing then set fixed indexing to isFixedIndexing
display dialog "Finished!" & return & (missingTracksCount as text) & " tracks didn't have artwork." buttons {"OK"} default button 1 with icon note giving up after 5
end tell

Applescript for controlling iTunes and Rdio

I have an Applescript I use in conjunction with Alfred that plays or pauses the current track in iTunes or Rdio, depending on which I have open. In my script Rdio takes precedence because I always have iTunes open and only open Rdio when I need it for a specific purpose.
Often, when a track is playing in iTunes and I hit my global shortcut to run this script it takes up to 15 seconds to stop the track. I wanted to share the script here and see if there might be a glaring issue, or if there is a much simpler, more efficient way to handle it.
I appreciate any help I can get!
tell application "System Events"
if (name of processes) contains "iTunes" then
set iTunesRunning to true
else
set iTunesRunning to false
end if
if (name of processes) contains "Rdio" then
set RdioRunning to true
else
set RdioRunning to false
end if
end tell
if RdioRunning then
tell application "Rdio"
if player state is paused or player state is stopped then
play
else if player state is playing then
pause
end if
end tell
else if iTunesRunning then
tell application "iTunes"
if player state is paused or player state is stopped then
play
else if player state is playing then
pause
end if
end tell
end if
It's difficult to track down such issues. In general your script looks fine. Here's some ideas that may help with your problem though.
In general applescripts are interpreted at run-time which means that every time you run your script the byte-code has to be changed into machine language code by another program (applescript runner)... this is normally not a problem but in your case maybe it's causing some slowness. So the idea is to write your script so that doesn't need to happen. We can do that by saving the script as an applescript application because applications are saved in the machine language form and thus do not require another program to execute the code. In addition we can take advantage that the commands for both applications are identical, so we can use a "using terms from" block. In your code you query system events for the "name of processes" twice, so the last optimization we can make is to only do that once.
So try this and see if it helps. I'm not certain it will but it's worth a try. Remember to save it as an application.
tell application "System Events" to set pNames to name of application processes
if "Rdio" is in pNames then
set appName to "Rdio"
else if "iTunes" is in pNames then
set appName to "iTunes"
else
return
end if
using terms from application "iTunes"
tell application appName
if player state is paused or player state is stopped then
play
else if player state is playing then
pause
end if
end tell
end using terms from
EDIT: If the above code doesn't work then try this. As mentioned, try it as an application and see if it helps. The same principles apply... one less query of system events and saving as an application to prevent needing to interpret the code.
tell application "System Events" to set pNames to name of application processes
if "Rdio" is in pNames then
tell application "Rdio"
if player state is paused or player state is stopped then
play
else if player state is playing then
pause
end if
end tell
else if "iTunes" is in pNames then
tell application "iTunes"
if player state is paused or player state is stopped then
play
else if player state is playing then
pause
end if
end tell
end if

Resources