Hammerspoon / hammerspoon

Staggeringly powerful macOS desktop automation with Lua
http://www.hammerspoon.org
MIT License
11.88k stars 578 forks source link

is there a way to detect when itunes starts playing a new track? #2273

Closed jrwren closed 4 years ago

jrwren commented 4 years ago

is there a way to detect when itunes starts playing a new track?

If not, is there a way to poll for it every few seconds?

latenitefilms commented 4 years ago

I don't believe there's any notification when a song changes.

You could use a hs.timer to check for changes. Something along the lines of:

local lastTrack = hs.itunes.getCurrentTrack()
iTuneSongChecker = hs.timer.doEvery(1, function()
    local currentTrack = hs.itunes.getCurrentTrack()
    if currentTrack ~= lastTrack then
        print("track changed")
    end
    lastTrack = currentTrack
end)
toy commented 2 years ago

Stumbled by accident and it may be very late, but one of following (or both) should do what you need:

itunesNotifications = hs.distributednotifications.new(function(name, object, userInfo)
  hs.alert(hs.inspect({'itunes', name, object, userInfo}))
end, 'com.apple.iTunes.playerInfo', 'com.apple.iTunes.player'):start()

musicNotifications = hs.distributednotifications.new(function(name, object, userInfo)
  hs.alert(hs.inspect({'music', name, object, userInfo}))
end, 'com.apple.Music.playerInfo', 'com.apple.Music.player'):start()
jrwren commented 2 years ago

This is great.

I have a start on a last.fm audioscrobbler.

It would be nice if there were a better way to do the auth, token fetching, and session key fetching, but this is a start.

local LASTFMAPIKEY = ''
local LASTFMAPISECRET = ''

-- get a token as described here: https://github.com/huberf/lastfm-scrobbler
-- http://www.last.fm/api/auth?cb=http://localhost:5555&api_key={YOUR_API_KEY}

local Token = ''
local sessionKey = '' --getSessionKey(Token)

local WSAPI = 'http://ws.audioscrobbler.com/2.0/'

local hashRequest = function(params, secret)
    local s = ''
    local a = {}
    for n in pairs(params) do table.insert(a, n) end
    table.sort(a)
    for i,k in ipairs(a) do
        s = s .. k
        s = s .. params[k]
    end
    s = s .. secret
    print ("hashing ", s)
    return hs.hash.MD5(s)
end

local urlcode_escape = function(str)
    return (str:gsub("\n", "\r\n"):gsub("([^0-9a-zA-Z ])", function(_) return string.format("%%%02X", string.byte(_)) end):gsub(" ", "+"))
end

local urlcode_encodetable = function(args)
    if args == nil or next(args) == nil then return "" end
    local results = {}
    for k, v in pairs(args) do
        if type(v) ~= "table" then v = { v } end
        for _, v2 in ipairs(v) do
            table.insert(results, urlcode_escape(tostring(k)) .. "=" .. urlcode_escape(tostring(v2)))
        end
    end
    return table.concat(results, "&")
end

local getSessionKey = function(token)
    local params = {}
    params['api_key'] = LASTFMAPIKEY
    params['method'] = 'auth.getSession'
    params['token'] = token

    local sig = hashRequest(params, LASTFMAPISECRET)
    print("hashed to sig ", sig)
    params['api_sig'] = sig
    params['format'] = "json"
    local urlencoded = urlcode_encodetable(params)
    print(urlencoded)
    local status, body, headers = hs.http.post(WSAPI, urlencoded, nil)
    print(status, headers, body)
    local jb = hs.json.decode(body)
    return jb.session.key
end

local scrobble = function(artist, title, album)
    local method = 'track.updateNowPlaying'
    local data = {
        artist = artist, 
        track = title,
        album = album,
        api_key= LASTFMAPIKEY,
        method= method,
        sk = sessionKey,
    }
    local sig = hashRequest(data, LASTFMAPISECRET)
    data['api_sig'] = sig
    data['format'] = 'json'
    local udata = urlcode_encodetable(data)
    hs.http.asyncPost('http://ws.audioscrobbler.com/2.0/', udata, nil, function(status, body, headers)
        print("updateNowPlaying response", status, body, headers)
        if status ~= 200 then return end

    end)
end

local musicNotifications = hs.distributednotifications.new(function(name, object, userInfo)
    local artist = userInfo["Artist"]
    local title = userInfo["Name"]
    local album = userInfo["Album"]
    local tt = userInfo["Total Time"]
    if tt == nil or tt < 30000 then
        return
    end
    if userInfo["Player State"] == "Playing" then
        hs.timer.doAfter(30, function()
            local ct = hs.itunes.getCurrentTrack()
            local pbs = hs.itunes.getPlaybackState()
            if ct ~= title or pbs ~= hs.itunes.state_playing then
                print("not scrobbling", ct, title, ct ~= title, pbs, hs.itunes.state_playing)
                return
            end
            scrobble(artist, title, album)
        end):start()
    end
    -- other known Player State values: "Paused"
end, 'com.apple.Music.playerInfo', 'com.apple.Music.player'):start()