Ruby & Cocoa: mooi en smakelijk?

Door CJ_Latitude op zondag 1 januari 2012 17:12 - Reacties (2)
Categorie: media center, Views: 4.669

Het is even sinds de laatste post, maar we waren druk met verbouwing en verhuizing, dus even geen tijd om hier mee bezig te gaan.

Als onderdeel van de inventarisatie van de verschillende mogelijkheden voor het maken van mijn media center software kwam ik ook op het pad van RubyCocoa (sourceforge). Hiermee kan je Objective-C objecten die onder OS-X beschikbaar zijn met Ruby manipuleren (en andersom, maar daar heb ik niet naar gekeken).

Waarom is dit relevant? Omdat iTunes gebruik maakt van het notificatieframework van OS-X (klik hier voor een introductie in dit framework). Het gebruikt dit om andere applicaties te laten weten wanneer er iets aan de state van iTunes veranderd (track dat speelt, volume, etc). Aangezien ik bezig was met Ruby en iTunes (zie eerdere posts) was het opvangen van deze notificaties een must.

Nu wordt Ruby standaard meegeleverd op OS-X, alsmede het RubyCocoa framework. Alles wat je nodig hebt om het te gaan gebruiken is:

code:
1
require 'osx/cocoa'



Vanaf dat moment zijn de objecten die in Objective-C beschikbaar zijn, ook in Ruby vorm aanwezig. Om zelf objecten te maken die gebruikt kunnen worden binnen functies van Cocoa, moeten ze overerven van OSX::NSObject:

code:
1
2
class MyFirstCocoaClass < OSX::NSObject
end



Zodra je weet hoe het notification center van OS-X werkt, hoef je voor een groot deel enkel syntax aan te passen om de calls die je in Objective-C zou maken in Ruby te maken. Zie onderstaande voorbeeld om de notificaties voor het wisselen van het afgespeelde track op te vangen:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
require 'osx/cocoa'

# Nodig om een 'shared state' in te stellen. Zonder dit werkt het niet, weet eigenlijk niet waarom...
@@application = OSX::NSApplication.sharedApplication

# Dit is de class die de notificaties uiteindelijk verwerkt
class ITunesNotifier < OSX::NSObject
  
  def notify(notification)
    track_info = notification.userInfo
    
    puts "Track changed:"
    puts "  artist: #{track_info['Artist']}"
    puts "  title:  #{track_info['Name']}"
    puts "  album:  #{track_info['Album']}"
  end
  
end

# Draadje, geleend van Meow
def start_runner
  @@background_runner = Thread.new do
    sleep 0.1
    OSX::NSApp.run
  end
  at_exit do
    loop do
      sleep 1
      @@application.terminate(@@application) if false
    end
  end
end

# Bind de playerInfo notification aan de ITunesNotifier#notify methode
OSX::NSDistributedNotificationCenter.defaultCenter.objc_send(
  :addObserver, ITunesNotifier.new,
  :selector,    "notify:",
  :name,        "com.apple.iTunes.playerInfo",
  :object,      nil
)

# Start het draadje
start_runner



Op het moment dat er een 'com.apple.iTunes.playerInfo' notificatie rond wordt gestuurd, wordt de #notify methode van de ITunesNotifier aangeroepen met als enigste argument een NSNotification instantie, waar in het userInfo object alle inhoudelijke informatie over de notificatie staat.

Een gedeelte van bovenstaande code is gebaseerd op / geleend van Meow, wat een library is om vanuit Ruby Growl notificaties mogelijk te maken. Hoe gaaf is dat!? :D

Wat kan je met OS-X, iTunes, Ruby on Rails en AppleScript?

Door CJ_Latitude op zaterdag 19 november 2011 20:39 - Reacties (6)
Categorie: media center, Views: 5.036

Zoals in de introductie zal ik het een en ander noteren over de verschillende mogelijkheden die je hebt als je zelf software wil maken voor het afspelen van media.

Van veel van de paden die ik heb belopen ben ik uiteindelijk weer afgestapt, om meerdere redenen. Vandaag het pad "Een Ruby on Rails app op OS-X". Primaire reden dat dit voor mij persoonlijk uiteindelijk niet geschikt was:
  • Op OS-X kan je geen BluRay discs afspelen (als in: insert, play, menu gebruiken, the whole package);
  • Het touchscreen wat ik heb verloor steeds zijn kalibratie, wat hem dus praktisch onbruikbaar maakte.
Totdat ik achter deze twee feiten was, heb ik echter wel veel geleerd over AppleScript en iTunes. Vandaag een introductie in deze combinatie.

Disclaimer: omdat dit al enige tijd geleden is, heb ik helaas niet meer de kennis voorhanden om zeer diep op deze mogelijkheden in te gaan. Ik zal proberen een globaal overzicht te geven en genoeg pointers om lezers op weg te helpen

Ruby on Rails en OS-X
Toen ik met het eerste probeersel begon waren er twee zekerheden:
- Het wordt een Ruby on Rails app;
- En zal draaien op OS-X.

Waarom? Ik vind het OS fijn en de taal en het framework ook. Simple as that. Er zijn echter niet veel zekerheden in het leven en achteraf gezien vielen bovenstaande hier dan ook zeker niet onder. Desalniettemin was ik daar in den beginne nog wel van overtuigd en ging enthausiast aan het werk om op deze basis wat moois in elkaar te draaien.

iTunes
Aangezien ik mijn muziekcollectie beheer en afspeel vanuit iTunes en deze applicatie daarom dus al over de benodigde info over de muziekbestanden bevat, leek het mij aardig om deze applicatie dan ook als bron van mijn gegevens te gebruiken.
Daarnaast leek het zelf in Ruby een audio-player maken mij onbegonnen werk en koos ik ook voor het bedienen van iTunes voor het afspelen van de muziek. Op OS-X heb je daar gewoon AppleScript voor. Door middel van osascript calls uit te voeren vanuit een Ruby exec statement kan de output van de AppleScript worden verzameld.
Let wel, op dit punt was Spotify voor mij nog niet in beeld, dus was er nog geen noodzaak om deze streams ook af te kunnen spelen.

Uitlezen en bedienen met AppleScript
Ik was positief verbaasd over de hoeveelheid functies die je op iTunes uit kan voeren met behulp van AS. Van simpele zaken zoals het starten / pauseren van een track:

code:
1
2
3
tell application "iTunes"
    playpause
end tell


naar het achterhalen van de huidige track naam:

code:
1
2
3
tell application "iTunes"
    name of current track
end tell


tot het maken van een playlist van een bepaald album:

code:
1
2
3
4
tell application "iTunes"
  make new playlist with properties {name:"Bran Van 3000 - Discosis"}
  duplicate (every track of library playlist 1 whose album is "Discosis") to playlist "Bran Van 3000 - Discosis"
end tell


Om te bekijken welke functies allemaal mogelijk zijn, kan je in de Script Editor naar Window --> Library gaan en dan iTunes selecteren.

Ene Doug Adams heeft het zijn levenswerk gemaakt om allerlei handige AS voor iTunes te maken en te verzamelen op zijn site: Doug's AppleScripts for iTunes. Hier heb ik veel handige truukjes gevonden.

Om gegevens van alle tracks in de library in te laden, kan je bijvoorbeeld het volgende doen:

code:
1
2
3
tell application "iTunes"
  get {name, artist, persistent ID} of every track in library playlist 1
end tell


Dit levert een array met drie arrays daarin op, met daarin respectievelijk de titels, artiesten en IDs van de tracks:

code:
1
2
3
4
5
{
  {"Hunting High And Low", "Take On Me", ...},
  {"a-ha", "a-ha", ...},
  {"90A37571882FC926", "B774C3835442DD90", ...}
}


De snelheid waarmee dit kan was (in ieder geval op mijn machine en met mijn library-formaat) prima werkbaar.

XML library
Een andere methode is het uitlezen van de XML-versie van de library. @Default staat deze in ~/User/Music/iTunes/iTunes Music Library.xml. De structuur van dit bestand is (gelukkig) self-explanatory. #Protip: als je XML met Ruby wil gaan parsen, gebruik dan Nokogiri en niet de default REXML. Scheelt ook weer een factor 20 in snelheid.

Muziek starten
Eenmaal uitgelezen heb je alle gegevens die je nodig hebt om een track te selecteren en uit te lezen. Dit draait dan voornamelijk om de PersistentID van het track:

code:
1
2
3
tell application "iTunes"
  play (some track of library playlist 1 whose persistent ID is "90A37571882FC926")
end tell


Met de delen die hierboven zijn uitgelegd (en een eigen beetje uitzoekwerk) zou je dus bijvoorbeeld ook een playlist van alle tracks van één album kunnen maken en het eerste nummer van die playlist af gaan spelen, zodat de playback stopt als het album voorbij is.

Zo, dat was een korte introductie in het bedienen van iTunes dmv AppleScript. In het volgende deel: het opvangen van notificaties vanuit iTunes mbv NSNotifications binnen RubyCocoa.

Comments altijd welkom!

Zelfbouw Software Media Center : Intro

Door CJ_Latitude op maandag 14 november 2011 11:43 - Reacties (17)
Categorie: media center, Views: 9.989

"Waarom?" is de vraag die ik vaak gekregen heb toen ik bekenden/vrienden/collegea vertelde dat ik zelf mijn eigen media center software aan het schrijven was. En terecht natuurlijk.

Want er zijn best veel goede, kant-en-klare mogelijkheden: Plex, XBMC, Boxee, Windows Media Center om er maar een paar te noemen. Niks mis mee, zou je zeggen. Ik zag echter wel wat tekortkomingen. Toen ik begon met deze operatie (maart 2010...) voldeed er geen aan ieder van deze eisen:
  • Bedienbaar zonder het aanzetten van de TV.
    Ik vind het zo suf als ik om een liedje te luisteren de TV aan moet zetten. Leuk zo'n 10ft interface, maar daar heb je niks aan als de TV niet aan staat
  • Bedienbaar met smartphone / tablet / touchscreen
    Ik heb nog een 15" industrial touchscreen liggen die ik in een muur wil bouwen, zodat er wel een vast controle-punt aanwezig is naast de smartphones en tablet die in het huis liggen
  • Interface op scherm, beeld naar TV
    Nog zo'n nadeel van de 10ft interface als je dat touchscreen wil gebruiken: het is niet te doen om op het ene scherm de browse-interface te hebben en dan de media (film / foto) naar de TV te laten sturen
  • Goede Spotify integratie
    Ik wil goede integratie. Ofterwijl: als ik mijn lokale lijst van Simon & Garfunkel nummers zie, moeten de nummers die Spotify van hen heeft erbij komen te staan, zonder dat ik daar iets voor hoef te doen
  • Aansturen van versterker
    Versterker automatisch aan/uit zetten (us ben zuunig!), speakers/sub schakelen
  • Aansturen van TV
    Dmv EPG op control-device zappen en automatisch zappen als programma begint
  • Mogelijkheid tot afspelen en bedienen BR-discs
    Niet alleen rips, maar ook de echte schijfjes
  • Automatisch tonen van media van USB device
    Inpluggen en de drive verschijnt in de interface, klaar om de media te tonen. Geen gedoe met kopiëren.
Veel van deze eisen komen op één punt neer: dat je niet van je luie reet af hoeft te komen om ook maar iets te doen zoals apparaten schakelen.
En daarnaast moet het ook bedienbaar zijn door de vrouw des huizes :)

Ondertussen hebben de bestaande pakketten ook niet stilgezeten en ben pas nog verleid tot het proberen van Plex met de nieuwe Plex App voor iPhone/iPad. Mooi spul, bovenstaande punten blijven veelal hierboven kunnen niet afgevinkt worden. Belangrijkste nadeel was dat de Spotify intergratie matig was (niet geintegreed met de lokale muziekbestanden) en dat Spotify niet eens op de remote werd getoond.

Nu zijn we dus al 1.5 jaar verder en hebben we nog niet eens een beta, laat staan een final versie. Vanwege veranderende eisen en problemen die ik tegenkwam zijn we nu al met de vijfde iteratie bezig. Een kort overzicht:
  1. Rails app met webinterface op OS-X Maar: touchscreen werkte niet goed op OS-X, geen BR discs in OS-X)
  2. Rails app met webinterface op Windows Maar: Rails werkt traaaaaaaaag op mijn machine op Windows, deze iteratie van webinterface was ook te traag)
  3. Monolitisch C# windows applicatie Maar: liep vast op niet goed uitgedachte architectuur, geen zin om meerdere clients te schrijven voor tablet/smartphone/touchscreen
  4. C# server met webinterface & websockets Werkt eigenlijk best goed, zag er super uit (Remote app van Apple nagebouwd), maar is net niet snappy genoeg en de websocket verbinding managen op de iPad / iPhone is lastig
  5. C# server met DACP Digital Audio Control Protocol: protocol van Apple die gebruikt wordt door hun native Remote app. Ofterwijl: de native app gebruiken voor m'n eigen server ipv iTunes / AppleTV. Pluspunt: er zijn ook Android client implementaties (zoals TunesRemote)
Nu dus bezig met nr. 5 en de potentie is torenhoog.

Het is een lang en soms frustrerend proces. Voordeel: ontzettend veel geleerd en wijzer geworden. Ik zal op dit blog verslag doen van mijn ervaringen, leermomenten en natuurlijk gewoon gaaf spul laten zien en code delen waar anderen hopelijk ook nog wat aan hebben.