• Apfeltalk ändert einen Teil seiner Allgemeinen Geschäftsbedingungen (AGB), das Löschen von Useraccounts betreffend.
    Näheres könnt Ihr hier nachlesen: AGB-Änderung
  • Was gibt es Schöneres als den Mai draußen in der Natur mit allen Sinnen zu genießen? Lasst uns teilhaben an Euren Erlebnissen und macht mit beim Thema des Monats Da blüht uns was! ---> Klick

AppleScript wirklich nur ausführen, wenn Programm gestartet und bereit

naich

Pomme d'or
Registriert
22.11.08
Beiträge
3.082
Hallo,

Folgende Situation: Ich lasse mir per Nerdtool den aktuellen Titel und weitere Infos von iTunes auf dem Desktop anzeigen, wenn es läuft.

Nun ist es immer wieder ärgerlich, dass iTunes sich recht oft wieder öffnet, direkt nachdem es geschlossen wurde - weil das AppleScript wohl läuft und iTunes mit
Code:
 tell application "iTunes"
wieder starten lässt.
Ich habe etliche Wege an Anfragen probiert, ob iTunes an ist, und keine funktioniert 100%. Da muss es doch irgendwas geben, was wirklich zuverlässig ist?!?

Folgendes habe ich schon probiert:
Code:
if running of application "iTunes" then...
Code:
tell application "System Events"
    set apps to every process whose bundle identifier is equal to "com.apple.iTunes"
    if (apps is not {}) then
Vorher nen Shell-Script starten, was dies überprüft:
Code:
#!/bin/sh
app=$1
scpt=$2
if [[ -n "`ps -x | grep /Applications/${app}.app | grep -v grep`" ]]; then
  osascript $scpt | iconv -f utf-8 -t ucs-2-internal
fi
...und auch noch weitere. Ist es nicht irgendwie möglich, das so hinzubiegen, dass es wirklich 100% funktioniert?
Also sowas wie:
Code:
is application "iTunes" ready...
Das AppleScript wird im Moment alle 5 - 10 Sekunden neu aufgerufen, und scheinbar ist die Zeit wohl so kurz, dass es immer wieder zu "Kollisionen" kommt, will ich iTunes per Cmd+Q schließen will...
 
Zuletzt bearbeitet:

toc-rox

Weigelts Zinszahler (Rotfranch)
Registriert
15.11.09
Beiträge
247
Ich habe eine ähnliche Problemstellung und wäre ebenfalls an einer Lösung interessiert.

Möglicherweise ist das Abfragen des Processflags ein Ansatz (16386 ist im Beispiel der iTunes-Prozeß):
Abfrage des Prozessstatus: ps -o uid,pid,state,flags,command 16386
Code:
  UID   PID STAT        F COMMAND
  505 16386 U        4000 /Applications/iTunes.app/Contents/MacOS/iTunes -psn_0_585871
  UID   PID STAT        F COMMAND
  505 16386 R        4000 /Applications/iTunes.app/Contents/MacOS/iTunes -psn_0_585871
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386  E       6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386 Z        6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386 Z        6000 (iTunes)
  UID   PID STAT        F COMMAND
  505 16386 Z        6000 (iTunes)
  UID   PID STAT        F COMMAND
  UID   PID STAT        F COMMAND

Erläuterung der Flags (Auszug):
Code:
P_WEXIT		   0x02000	Working on exiting
P_EXEC		   0x04000	Process called exec

M.E. sieht man am Flag "P_WEXIT", daß der Prozeß ein Stopsignal erhalten hat und sich gerade beendet.
Im Beispiel oben sieht man dies m.E. am Wert 6000.

Hier könnte m.E. das Problem der bisherigen Lösungen liegen - während der Stopphase (der Prozeß läuft noch) veranlaßt das AppleScript das erneute Starten des Prozeßes. D.h. der Prozeß stopp zwar, startet aber sofort wieder neu.

Fragen an die Systemexperten:
- Ist die Annahme mit dem P_WEXIT-Flag korrekt?
- Könnte man alternativ auch den State "E" nutzen?

Hier noch mein kleines Testskript:
Code:
#!/bin/bash
for i in {1..5000}
do
   ps -o uid,pid,state,flags,command 16386
done
 

fyysh

Schweizer Glockenapfel
Registriert
25.01.10
Beiträge
1.386
Schüsschen ins blaue:

Habt ihr schon sowas probiert?
Code:
tell app "system events" to set iTunesRunning to (name of every process contains "iTunes")
if (iTunesRunning) then
tell app "itunes"
-- ... blabla whatever
end
end


oder in nem shell script eingewebt:
Code:
#!/bin/bash

function iTunesRunning() {
osa_result="$(/usr/bin/osascript <<-MYOSA
tell app "system events" to set iTunesRunning to (name of every process contains "iTunes")
if (iTunesRunning) then
return 0
else
return 1
end
MYOSA
)"
return $osa_result
}

if iTunesRunning; then
echo "yeah baby, itunes rennt"
else
echo "nope, itunes rennt nicht"
fi
 
Zuletzt bearbeitet:

naich

Pomme d'or
Registriert
22.11.08
Beiträge
3.082
@fyysh: Beides schnell ausprobiert, funktioniert genauso wenig gut wie meine Beispiele oben.

So wie ich das verstehe, darf die Abfrage und das eigentliche ausführen des Skripts zeitlich nicht weit auseinanderliegen, sonst besteht eher das Problem. Nur durch die 2 eigenen osascript-Prozesse im 2. Beispiel (nach der if-Abfrage dann das Script gestartet) ist dem ja genau so.

@toc-rox: Klingt interessant, viell. kann man sich ja daraus was sinnvolles basteln...

Edit:
Ich hab mal eben versucht, die Infos in das Shell-Skript reinzubasteln:
Code:
#!/bin/sh
app=$1
scpt=$2

outp="`ps -o flags,command -x | grep /Applications/${app}.app/Contents/MacOS/${app} | grep -v grep | awk '{print $1}'`"
#echo $outp
if [[ outp && "$outp" != "6000" ]]; then
  osascript $scpt #| iconv -f utf-8 -t ucs-2-internal
fi

Leider scheint das auch noch nicht die optimale Lösung zu sein, iTunes startet recht oft wieder neu.
 
Zuletzt bearbeitet:

fyysh

Schweizer Glockenapfel
Registriert
25.01.10
Beiträge
1.386
Verstehe ich nicht. Was macht denn dieses Nerd-Tool?

Weil schau mal das:
Code:
#!/bin/bash

function get_itunes_track(){
	osa_result="$(/usr/bin/osascript <<- MYOSA
		tell application "System Events" to set iTunesRunning to (name of every process contains "iTunes")

		if (iTunesRunning) then
			tell application "iTunes"
				try
					set myResult to (name of current track)
				on error
					set myResult to "Nothing playing"
				end try
			end tell
		else
			set myResult to "iTunes not running"
		end if
		
		return myResult
		MYOSA
	)"
	echo "$osa_result"
}


get_itunes_track
gespeichert als bspw. gitt.sh und ausgeführt in der bash und war so:
Code:
while true;do ./gitt.sh;sleep 1;clear;done
funktioniert wunderbar.

Erkennt ob iTunes läuft, ob was läuft, wenn ja wie das heißt, was da läuft UND startet iTunes NICHT, wenn es nicht läuft, was ja dein eigentliches Problem war... ^^
 

naich

Pomme d'or
Registriert
22.11.08
Beiträge
3.082
Verstehe ich nicht. Was macht denn dieses Nerd-Tool?

Führt (im Moment) folgende Kommandozeile alle 1 (oder 5) Sekunden aus:
Code:
osascript /Applications/AppleScript/iTunes.scpt

Kompletter Code des Skripts (momentan):
Code:
tell application "System Events"
    set apps to every process whose bundle identifier is equal to "com.apple.iTunes"
    if (apps is not {}) then
  
        --if running of application "iTunes" then
        tell application "iTunes"
            try
                set playerstate to (get player state)
            end try
            if playerstate = paused then
                set trackPaused to " (paused)"
            else
                set trackPaused to ""
            end if
            if playerstate = stopped then
                return "Stopped"
            end if
            
            set spacer to "        "
            
            set trackID to the current track
            set trackName to the name of trackID
            set artistName to the artist of trackID
            set albumName to the album of trackID
            
            set totalData to "Track :" & spacer & trackName & trackPaused & "
Artist :" & spacer & artistName & "
Album :" & spacer & albumName
            return totalData
        end tell
    else
        return ""
    end if
    
end tell
Vielleicht findest du oder jemand anders das Problem ja im eigentlichen Skript...


... UND startet iTunes NICHT, wenn es nicht läuft, was ja dein eigentliches Problem war... ^^

Es geht insbesondere um den Fall, wenn du, während das Skript läuft, iTunes beendest. Dann startet iTunes manchmal sofort ohne weiteres Zutun neu. Teste das mal.
 
Zuletzt bearbeitet:

fyysh

Schweizer Glockenapfel
Registriert
25.01.10
Beiträge
1.386
Hab das Problem gerade in diesem Spaßscript gehabt und folgendermaßen gelöst:

Du musst mal gucken, wie lange iTunes bei dir braucht, um runterzufahren. Bei mir braucht es ca. 4.etwas Sekunden, biss es nicht mehr als "laufend" gemeldet wird. Wenn du deinen sleep höher als das setzt, ich hab 5s genommen, dann klappt's. Solltest du iTunes noch während dem "quitten" erwischen, kommt's wieder hoch.

Wahrscheinlich kommste über die Scripting Bridge besser an die benötigten Infos - oder du machst es dir eben einfacher und erhöhst den Intervall, mit dem du nach "iTunes rennt" checkst. :)
 

naich

Pomme d'or
Registriert
22.11.08
Beiträge
3.082
Wo willst du nen Sleep setzen? Ich versteh grad nicht ganz.
Das Problem ist ja folgendes: NerdTool ruft alle 5 Sec. das Skript auf.

Wenn ich jetzt nun iTunes beende, kann es sein, dass das Skript erst vor ner Sekunde oder so lief --> iTunes bleibt zu
Wenn das Skript aber in ner halben Sekunde startet und ich nun iTunes schließe --> iTunes geht wieder auf.

Sprich wenn ich die Zeit hochsetze, ist dies keine richtige Lösung für das Problem, es verringert nur die Wahrscheinlichkeit, dass es zu diesen "Kollisionen" kommt.

Von welchen Infos redest du genau, und was meinst du mit Scripting Bridge?
 

fyysh

Schweizer Glockenapfel
Registriert
25.01.10
Beiträge
1.386
Das NerdTool, startet es das Script im Hintergrund oder wartet es, bis es ausgeführt ist?

Wenn Zweiteres, stellst du den loop Intervall im NerdTool einfach auf kleiner, was weißt ich, 1s bspw., und baust dir dein intervall, in dem iTunes noch laufen soll, in dein AS.

Anders gesagt: Du startest zwar alle Sekunde dein AS, das aber checkt am Anfang ob iTunes rennt, wartet dann 5s und checkt nochmal ob iTunes rennt. Wenn es das zweite Mal noch rennt, dann agiert es erst.

Mit ner while schleife funzt's.

Code:
if (checkIf_iTunesRunning()) then
	delay 5
else
	return
end if


if (checkIf_iTunesRunning()) then
	
	--if running of application "iTunes" then
	tell application "iTunes"
		try
			set playerstate to (get player state)
		end try
		if playerstate = paused then
			set trackPaused to " (paused)"
		else
			set trackPaused to ""
		end if
		if playerstate = stopped then
			return "Stopped"
		end if
		
		set spacer to "        "
		
		set trackID to the current track
		set trackName to the name of trackID
		set artistName to the artist of trackID
		set albumName to the album of trackID
		
		set totalData to "Track :" & spacer & trackName & trackPaused & "\nArtist :" & spacer & artistName & "\nAlbum :" & spacer & albumName
		return totalData
	end tell
else
	return ""
end if


on checkIf_iTunesRunning()
	tell application "System Events" to set iTunesRunning to (bundle identifier of processes contains "com.apple.iTunes")
	return iTunesRunning
end checkIf_iTunesRunning
 

fyysh

Schweizer Glockenapfel
Registriert
25.01.10
Beiträge
1.386
Ach quatsch, gehüpft wir gesprungen.

aber warte, das muss doch irgendwie gehen... moment... *g
 

fyysh

Schweizer Glockenapfel
Registriert
25.01.10
Beiträge
1.386
mir scheints fast, dass du da nur mit objc an die scripting bridge dran kommst.allerdings fragt man in objc auch nur "mal eben" ab, ob itunes rennt und macht dann die aktionen. eigentlich müsste dann da dasselbe passieren, wenn der man die scripting bridge zur falschen zeit abfragt.
Testing for Launched Applications

If an application isn’t executing when Scripting Bridge tries to send it an Apple event, Scripting Bridge may automatically launch it. The launch of the other application may come as a surprise to your users, along with the fact that you application’s execution is blocked while the other application is launching.

Because of these potential surprises, it’s often a good idea to check whether the target application is running before you try to communicate with a Scripting Bridge message. Suppose you want to get the name of the current track, but only if iTunes is running. You could accomplish by first testing for application execution with the isRunning of SBApplication, as shown in Listing 3-4.

Listing 3-4 Testing for application execution
- (NSString *) nameOfCurrentTrack
{
// "iTunes" is an instance variable
if ([iTunes isRunning]) {
return [[iTunes currentTrack] name];
}
return nil;
}


mit applescript, ruby und perl hab ich es jetzt nicht geschafft 100% immer dafür zu sorgen, dass itunes nicht nochmal startet. wenn du, egal in welcher sprache, die scripting bridge zur falschen zeit ansprichst, startet itunes neu. wenn da jemand ne 100% lösung dafür hat: her damit. :)


mit ist beim testen aufgefallen, dass itunes länger für den shutdown braucht, wenn man es im status "playing" schließt. wenn ich status "stopped" habe, habe ich keinen relaunch bekommen - wahrscheinlich habe ich aber nicht den richtigen moment getroffen, um den relaunch triggern.

mit applescript (ruby & python) lässt sich das, uhm, "risiko des relaunches" also höchstend minimieren, nicht ausschließen. scheinbar.

ha! witzig. mit ist grad beim exzessiven testen aufgefallen, dass apps, die eigentlich die oben genannte objc methode nutzen müssten (apps sind ebenfalls oben genannt), um die infos von itunes zu bekommen, durchaus ebenfalls in der lage sind, die scripting bridge genau im falschen moment anzusprechen, so dass itunes neu startet.

ich hab trotzdem ne methode gefunden, wie man es evtl funktionieren _könnte_. ist aber ein ganz schönes gewurschtel.

1. du schreibst dir n applescript und speicherst es als app mit der "stay open" option. mit folgendem inhalt (natürlich angepasst, ist jetzt nur ein beispiel - you'll get the point):
Code:
on run
	-wenn's startet
	showittrackname()
end run

on idle
	--wenn's nix zu tun hat
	delay 10
	showittrackname()
end idle

on quit
	--wenn's beendet wird
	tell application "iTunes" to quit
	continue quit
end quit


on reopen
	--wenn focus weg war und es den focus zurückbekommt
	tell application "iTunes" to activate
end reopen


on showittrackname()
	repeat
		try
			tell application "iTunes" to set cs to name of current track
			exit repeat
		on error
			tell application "iTunes" to play
		end try
	end repeat
	
	try
		display dialog cs giving up after 5
	on error
		tell me to quit
	end try
end showittrackname

2. jetzt launchst du itunes nicht über itunes.app, sondern über die so erstellte app. das app launcht dann itunes.

3. das app passt du natürlich so an, dass es dir nicht ständig einen dialog mit dem namen vom current track anzeigt, das nervt eh nur ;), sondern dass es die die infos die du für das nerdtool brauchst irgendwohin legt, so dass du dann mit dem nerdtool bequem auf diese zugreifen kannst.

4. du remapst dir die keys für itunes so, dass quit nicht mehr cmd+q ist. statdessen legste dir ein service oder so an den du bei itunes mit cmd+q triggerst. dieser service tut dann nix anderes, als das script zu beenden. ich weiß aber jetzt nicht, ob dich die preferences sowas anstellen lassen. mit so nem tool wie keyboard maestro oder quickeys würde es funktionieren (der remap von cmd+q). wobei mit solchen tools bräuchtest du das gar nicht, weil diese auch das quit event einer app abfangen können und dann eine aktion ausführen. wenn man das so machen würde, müsste man aber das "tell app itunes to quit" aus dem on quit handler rausschmeißen, sonst findet das wahrscheinlich doppelt statt. allgemein könnte man sich das leben mit so nem tool ziemlich vereinfachen, da die auch den open event erkennen. dann lässt man das script einfach mitstarten und alles andere bleibt einfach so, wie es ist.

5. das script selbst, wie du dem code entnehmen kannst, beendet erst itunes und dann sich selbst.

6. wenn du das script dann im dock nicht sehen willst, müsstest du in seiner info.plist noch "lsuielement" hinzufügen und auf true setzen.

natürlich müsste man das script noch um einige dinge erweitern, wie z.b. checken, ob itunes läuft und sich selbst beenden, wenn nicht (und zwar ohne itunes zu quitten, sonst startet itunes ja wieder...) usw. müsste man schon noch was reinstecken. ;)

sollte aber eigentlich funktionieren. ohne garantie natürlich, dass es auf lange dauer funzt, hab's nur kurz ausprobiert. aber das restart-problem wäre damit eigentlich erledigt - solange nix anderes auf die scripting bridge zugreift, natürlich.

ist aber doch komplizierter, als ich dachte ;)
 

LittlePixel

Strauwalds neue Goldparmäne
Registriert
09.07.08
Beiträge
641
Hallo,

mir scheints fast, dass du da nur mit objc an die scripting bridge dran kommst.allerdings fragt man in objc auch nur "mal eben" ab, ob itunes rennt und macht dann die aktionen. eigentlich müsste dann da dasselbe passieren, wenn der man die scripting bridge zur falschen zeit abfragt.
Das ist bedingt richtig.
Leider hat die ScriptingBridge einen Bug, so das -isRunning Dir die eigentliche Frage nie beantworten kann.
Ist in dem Fall iTunes geschlossen, so wird es geöffnet daraufhin -isRunning ausgeführt und gibt Dir dann YES zurück.
Somit ist die Methode leider nicht sinngemäß.

Besser ist auch hier:
Code:
// Ist iTunes offen?
+(BOOL)isYourTunesRunning
{
	
    NSArray *openAppsIdentifier = [[[NSWorkspace sharedWorkspace] launchedApplications] valueForKey:@"NSApplicationBundleIdentifier"];
	
	if([openAppsIdentifier containsObject:"com.apple.iTunes"])
	{
		
		return YES;
		
	}

	return NO;
	
}

Das würde aber immer noch nicht euer Problem lösen.

Angenommen wir reden vom Start einer Applikation, dann informiert das Programm einen was es gerade macht.

http://developer.apple.com/mac/libr...ce.html#//apple_ref/doc/uid/20000391-BCIEFIDI

Für euch wichtig ist hier NSWorkspaceWillLaunchApplicationNotification und NSWorkspaceDidLaunchApplicationNotification.

Als erstes wird -will und dann -did ausgeführt. ... und genau in dem Zeitraum zwischen den zwei Nachrichten würde euch die Abfrage, ob das Programm läuft, ja liefern, obwohl es noch nicht ansprechbar ist.
D.h. erst nach -did ist es für euch verfügbar.

Leider könnt ihr das mit AS aber nie richtig sicherstellen.

Viele Grüße
 
  • Like
Reaktionen: fyysh

fyysh

Schweizer Glockenapfel
Registriert
25.01.10
Beiträge
1.386
Das ist bedingt richtig.
Leider hat die ScriptingBridge einen Bug, so das -isRunning Dir die eigentliche Frage nie beantworten kann.
Ist in dem Fall iTunes geschlossen, so wird es geöffnet daraufhin -isRunning ausgeführt und gibt Dir dann YES zurück.
Somit ist die Methode leider nicht sinngemäß.
Na großartig. Und schon wäre der Grund gefunden, warum das nicht klappt. *DOH*
BTW: Tolle Doku, Apfel... o_O

Danke für die Info!