/** * Copyright 2009 Marc Stogaitis and Mimi Sun * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmote.server.media.itunes; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; import org.gmote.common.media.MediaMetaInfo; import org.gmote.server.media.MediaCommandHandler; import org.gmote.server.media.MediaInfoUpdater; import quicktime.QTSession; import quicktime.app.view.GraphicsImporterDrawer; import quicktime.app.view.QTImageProducer; import quicktime.qd.QDRect; import quicktime.std.image.GraphicsImporter; import quicktime.util.QTHandle; import quicktime.util.QTUtils; import com.apple.cocoa.application.NSApplication; import com.apple.cocoa.foundation.NSAppleEventDescriptor; import com.apple.cocoa.foundation.NSAppleScript; import com.apple.cocoa.foundation.NSAutoreleasePool; import com.apple.cocoa.foundation.NSData; import com.apple.cocoa.foundation.NSMutableDictionary; @SuppressWarnings("deprecation") public class ItunesCommandHandler extends MediaCommandHandler { private static final Logger LOGGER = Logger.getLogger(ItunesCommandHandler.class .getName()); private static ItunesCommandHandler instance = null; private static MediaMetaInfo media = null; private static String album = null; protected ItunesCommandHandler() { NSApplication.sharedApplication(); } protected void closeMedia() { tellItunesTo("quit", 1); } protected void stopMedia() { tellItunesTo("stop", 1); } protected void rewind() { tellItunesTo("set player position to (player position - 12)", 1); } protected void fastForward() { tellItunesTo("set player position to (player position + 12)", 1); } protected void pauseMedia() { tellItunesTo("pause", 1); } protected void playMedia() { tellItunesTo("play\n\nset song repeat of current playlist to all", 1); MediaInfoUpdater.instance().sendMediaUpdate(getNewMediaInfo()); } protected void rewindLong() { tellItunesTo("previous track", 1); MediaInfoUpdater.instance().sendMediaUpdate(getNewMediaInfo()); } protected void fastForwardLong() { tellItunesTo("next track", 1); MediaInfoUpdater.instance().sendMediaUpdate(getNewMediaInfo()); } protected void setVolume(int volume) { tellItunesTo("set sound volume to " + volume, 1); } protected int getVolume() { for (int i=0; i<3; i++) { try { NSAppleEventDescriptor result = tellItunesTo("get sound volume", 1); return result.int32Value(); } catch(Exception e){ // getting volume failed, try again } } return 50; } protected void toggleMute() { tellItunesTo("set mute to (not mute)", 1); } protected void fullScreen() { String script = "tell application \"System Events\"\n" + "keystroke \"f\" using command down\n" + "end tell\n"; executeForResult(script, 1); } public List<String> getPlaylists() { List<String> playlists = new ArrayList<String>(); for (int times = 0; times < 3; times++) { try { NSAppleEventDescriptor result = tellItunesTo( "get the name of every playlist", 20); int numItems = result.numberOfItems(); for (int i = 1; i <= numItems; i++) { NSAppleEventDescriptor plDiscriptor = result.descriptorAtIndex(i); playlists.add(plDiscriptor.stringValue()); } break; } catch (Exception e) { } } return playlists; } public List<String> getTracksFromPlaylist(String playlist) { List<String> tracks = new ArrayList<String>(); for (int times = 0; times < 3; times++) { try { String script = "set theTracks to {}\n" + "repeat with aTrack in tracks of playlist \"" + playlist + "\"\n" + "copy (name of aTrack) & \"|\" & (kind of aTrack) to end of theTracks\n" + "end repeat\n" + "theTracks\n"; NSAppleEventDescriptor result = tellItunesTo(script, 1); int numItems = result.numberOfItems(); for (int i = 1; i <= numItems; i++) { NSAppleEventDescriptor trackDiscriptor = result.descriptorAtIndex(i); tracks.add(trackDiscriptor.stringValue()); } break; } catch (Exception e) { } } return tracks; } protected void launchAudio(String track, String playlist) { tellItunesTo("play track \"" + track + "\" of playlist \"" + playlist + "\"\nset song repeat of current playlist to all", 1); } protected void launchVideo(String name) { tellItunesTo("set visible of window 1 to true\nset frontmost to true", 1); tellItunesTo("set view of browser window 1 to playlist \"Movies\"", 1); tellItunesTo("play track \"" + name + "\" of playlist \"Movies\"", 1); fullScreen(); LOGGER.log(Level.INFO, "Launched movie"); } public synchronized MediaMetaInfo getNewMediaInfo() { try { NSAppleEventDescriptor result = tellItunesTo("get {name, artist, album} of current track", 1); String title = result.descriptorAtIndex(1).stringValue(); String artist = result.descriptorAtIndex(2).stringValue(); if (media != null && media.getTitle().equals(title) && media.getArtist().equals(artist)) { return null; } else { String newAlbum = result.descriptorAtIndex(3).stringValue(); media = new MediaMetaInfo(); media.setTitle(title); media.setArtist(artist); media.setAlbum(newAlbum); if (!newAlbum.equals(album)) { album = newAlbum; getArtwork(); } else { media.setImage(new byte[]{1}); } } return media; } catch(java.lang.NullPointerException e) { } catch(Exception e) { LOGGER.log(Level.INFO, e.getMessage(), e); } media = null; return media; } private void getArtwork() { NSAppleEventDescriptor result; try { int myPool = NSAutoreleasePool.push(); result = tellItunesTo("get data of artwork 1 of current track", 1); NSData data = result.data(); byte[] pictbytes = data.bytes(0, data.length()); byte []newpictbytes = new byte[512+pictbytes.length]; System.arraycopy(pictbytes,0,newpictbytes,512,pictbytes.length); if(QTSession.isInitialized() == false) { QTSession.open(); } QTHandle qt = new QTHandle(newpictbytes); GraphicsImporter gc = new GraphicsImporter(QTUtils.toOSType("PICT")); gc.setDataHandle(qt); QDRect qdRect = gc.getNaturalBounds(); GraphicsImporterDrawer myDrawer = new quicktime.app.view.GraphicsImporterDrawer(gc); QTImageProducer qtProducer = new QTImageProducer (myDrawer, new Dimension(qdRect.getWidth(),qdRect.getHeight())); Image img = Toolkit.getDefaultToolkit().createImage(qtProducer); BufferedImage bu = new BufferedImage(img.getWidth(null),img.getHeight(null),BufferedImage.TYPE_INT_RGB); Graphics g = bu.getGraphics(); g.drawImage(img,0,0,null); g.dispose(); ByteArrayOutputStream bas = new ByteArrayOutputStream(); ImageIO.write(bu, "PNG", bas); byte[] image = bas.toByteArray(); //// System.out.println("++ image size: " + image.length); media.setImage(image); NSAutoreleasePool.pop(myPool); } catch(java.lang.NullPointerException e) { } catch(Exception e) { LOGGER.log(Level.INFO, e.getMessage(), e); } finally { QTSession.close(); } } public static ItunesCommandHandler instance() { if (instance == null) { instance = new ItunesCommandHandler(); } return instance; } protected boolean running() { try { String script = "tell application \"System Events\"\n" + "set isRunning to ((application processes whose (name is equal to \"iTunes\")) count)\n" + "end tell\n" + "if isRunning is greater than 0 then\n" + "return true\n" + "else\n" + "return false\n" + "end if"; NSAppleEventDescriptor result = executeForResult(script, 3); System.out.println("running: " + result); return result.booleanValue(); } catch (Exception e) { LOGGER.log(Level.INFO, e.getMessage(), e); } return false; } public boolean isMediaOpen() { return running(); } protected boolean isMediaPaused() { return false; //TODO(mimi) } /* Run commands with iTunes */ private NSAppleEventDescriptor tellItunesTo(String actions, int timeout) { String script = "tell application \"iTunes\"\n" + actions + "\n end tell\n"; return executeForResult(script, timeout); } private synchronized NSAppleEventDescriptor executeForResult(String script, int timeout) { script = "with timeout " + timeout + " seconds\n" + script + "\nend timeout\n"; //LOGGER.log(Level.INFO, "\nExecuting script for result: " + script); // This creates a new NSAppleScript object // to execute the script NSAppleScript myScript = new NSAppleScript(script); // This dictionary holds any errors // that are encountered during script execution NSMutableDictionary errors = new NSMutableDictionary(); // Execute the script! NSAppleEventDescriptor results = myScript.execute(errors); // Print out results //if (results != null) { //LOGGER.log(Level.INFO, "Results: " + results.toString()); //} return results; } void launchFile(String fileName) { String fileNameForScript = fileName.replace('/', ':'); tellItunesTo("play file \"" + fileNameForScript + "\"", 2); } }