Skip to content

Instantly share code, notes, and snippets.

@seanlilmateus
Forked from Tatsh/example-as.js
Created May 16, 2020 07:29
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 seanlilmateus/18d11cfe678a450d94ed0ef891ca9bd8 to your computer and use it in GitHub Desktop.
Save seanlilmateus/18d11cfe678a450d94ed0ef891ca9bd8 to your computer and use it in GitHub Desktop.
Example AppleScript in JavaScript.
// Send a message to one person by name with Messages.app
function sendMessageTo(name, message) {
var app = Application('Messages');
var chats = app.chats(); // Get all the chats, regardless of service
var theChat;
for (var i = 0, len < chats.length; i < len; i++) {
chat = chats[i];
if (chat.participants().length > 1) { // Ignore 'rooms'
continue;
}
if (chat.participants()[0].name() === name) { // Compare name shown on buddy list/message list
theChat = chat;
break;
}
}
app.send(message, {to: theChat});
}
// Delete all songs in the iTunes library (not the file, just the database record)
function emptyiTunesLibrary() {
var app = Application('iTunes')
var lib, k;
for (k in app['sources']()) {
source = app['sources'][k];
if (source.name() === 'Library') {
lib = source;
break;
}
}
for (k in lib.tracks()) {
lib.tracks()[k].delete();
}
}
// Get a device source object by name from iTunes
function getDeviceByName(searchTerm) {
var app = Application('iTunes');
var source, k;
for (k in app['sources']) {
source = app['sources'][k];
if (source.kind() === 'iPod' && source.name() === searchTerm) {
return source;
}
}
throw new Exception('No device with name ' + searchTerm);
}
# use $.printf because that doesn't give an extra new line, unlike console.log
ObjC.import 'stdio'
ObjC.import 'stdlib'
ObjC.import 'AppKit'
ObjC.import 'Foundation'
NSUTF8StringEncoding = 4
running = false
# Check if iTunes is running
# First way, equivalent to `ps aux | egrep 'iTunes$'`
# pipe = $.NSPipe.pipe
# file = pipe.fileHandleForReading # NSFileHandle
# task = $.NSTask.alloc.init
# task.launchPath = '/bin/ps'
# task.arguments = ['aux']
# task.standardOutput = pipe
#
# task.launch
#
# data = file.readDataToEndOfFile # NSData
# file.closeFile
#
# data = $.NSString.alloc.initWithDataEncoding(data, NSUTF8StringEncoding)
# for line in ObjC.unwrap(data).split '\n'
# if /iTunes$/.test line
# running = true
# break
# Second way, with NSWorkspace
for app in ObjC.unwrap($.NSWorkspace.sharedWorkspace.runningApplications)
# alternative is to unwrap, and this first check is unneccesary:
# if ObjC.unwrap(app.bundleIdentifier) == 'com.apple.iTunes'
if typeof app.bundleIdentifier.isEqualToString == 'undefined'
continue
if app.bundleIdentifier.isEqualToString 'com.apple.iTunes'
running = true
break
if not running
#$.printf '/me is not playing anything at the moment\n'
$.exit 1
itunes = Application 'iTunes'
try
currentTrack = itunes.currentTrack()
catch
# No track playing
$.exit 0
$.printf "/me is listening to #{ currentTrack.artist() } - #{ currentTrack.name() } (via iTunes)"
ObjC.import 'stdio'
ObjC.import 'AppKit'
ObjC.import 'Foundation'
class FileNotFoundError extends Error
class iTunesNotRunningError extends Error
class iTunes
finder: Application 'Finder'
_library: null
running: ->
for app in ObjC.unwrap($.NSWorkspace.sharedWorkspace.runningApplications)
if typeof app.bundleIdentifier.isEqualToString == 'undefined'
continue
if app.bundleIdentifier.isEqualToString 'com.apple.iTunes'
return true
return false
constructor: ->
Object.defineProperty this, 'itunes', {
get: =>
if not @running()
$.NSWorkspace.sharedWorkspace.launchAppWithBundleIdentifierOptionsAdditionalEventParamDescriptorLaunchIdentifier(
'com.apple.iTunes',
$.NSWorkspaceLaunchAsync | $.NSWorkspaceLaunchAndHide,
$.NSAppleEventDescriptor.nullDescriptor,
null,
)
return Application('iTunes')
}
Object.defineProperty this, 'library', {
get: =>
if @_library
return @_library
for source in @itunes['sources']()
if source.name() == 'Library'
@_library = source
break
@_library
}
Object.defineProperty this, 'currentTrack', {
get: =>
@itunes.currentTrack
}
deleteOrphanedTracks: ->
ret = []
for track in @library.tracks()
name = track.name()
try
loc = track.location()
catch
$.printf "Removing #{ name }"
ret.push track
track.delete()
continue
if not loc or not @finder.exists loc
$.printf "Removing #{ name }"
ret.push track
track.delete()
ret
# root must be a Finder folder item
# finder.home().folders.byName('Music').folders.byName('import');
addTracksAtPath: (root) ->
paths = (Path(x.url().replace(/^file\:\/\//, '')) for x in root.entireContents())
@itunes.add paths, {to: @library}
# Refresh all tracks in case some changes do not get detected
for track in @library.tracks()
@itunes.refresh(track)
it = new iTunes()
dir = it.finder.home().folders.byName('Music').folders.byName('import')
console.log 'Deleting orphaned tracks'
deleted = it.deleteOrphanedTracks()
console.log 'Updating iTunes track list'
it.addTracksAtPath dir
# Update ratings
# Have to use .items for 'hidden' files
filePath = dir.items.byName('.ratings').url().replace(/^file\:\/\//, '')
ratingsFileData = $.NSString.stringWithContentsOfFileUsedEncodingError(filePath, $.NSUTF8StringEncoding, null)
ratings = {}
console.log 'Building basename:track hash'
for track in it.library.tracks()
loc = track.location().toString()
loc = ObjC.unwrap($.NSString.stringWithString(loc).lastPathComponent)
ratings[loc] = track
console.log 'Setting ratings'
for line in ObjC.unwrap(ratingsFileData).split '\n'
line = line.trim()
if not line
continue
spl = line.split ' '
filename = spl[1].trim()
rating = parseInt spl[0], 10
rating /= 5
rating *= 100
if filename not of ratings
throw new FileNotFoundError filename
ratings[filename]().rating = rating
ObjC.import 'AppKit'
class iTunesNotRunningError extends Error
class iTunes
finder: Application 'Finder'
_library: null
running: ->
for app in ObjC.unwrap($.NSWorkspace.sharedWorkspace.runningApplications)
if typeof app.bundleIdentifier.isEqualToString == 'undefined'
continue
if app.bundleIdentifier.isEqualToString 'com.apple.iTunes'
return true
return false
constructor: ->
Object.defineProperty this, 'itunes', {
get: =>
if not @running()
throw new iTunesNotRunningError('iTunes not found to be running')
return Application('iTunes')
}
Object.defineProperty this, 'library', {
get: =>
if @_library
return @_library
for source in @itunes['sources']()
if source.name() == 'Library'
@_library = source
break
@_library
}
Object.defineProperty this, 'currentTrack', {
get: =>
@itunes.currentTrack
}
clearOrphanedTracks: ->
ret = []
for track in @library.tracks()
name = track.name()
try
loc = track.location()
catch
$.printf "Removing #{ name }"
ret.push track
track.delete()
continue
if not loc or not @finder.exists loc
$.printf "Removing #{ name }"
ret.push track
track.delete()
ret
# root must be a Finder folder item
# finder.home().folders.byName('Music').folders.byName('import');
addTracksAtPath: (root) ->
paths = (Path(x.url().replace(/^file\:\/\//, '')) for x in root.entireContents())
@itunes.add paths, {to: @library}
# Refresh all tracks in case some changes do not get detected
for track in @library.tracks()
@itunes.refresh(track)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment