album art won't update [iTunes; image view]

i have a piece of code that displays the current song’s artwork in an image view. The only problem is, that it doesn’t update when their is a change of song.
heres what i have (it’s my whole “track info” method actually)

on tracktext()
	using terms from application "iTunes"
		tell application "iTunes"
			if exists (database ID of current track) then
				set cur_song to the name of the current track
				set cur_artist to the artist of the current track
				set cur_album to the album of the current track
				set cur_art to front artwork of the current track
				set art_data to data of cur_art
			else
				set cur_song to "No song"
				set cur_artist to "currently"
				set cur_album to "playing"
			end if
		end tell
	end using terms from
	set tempartwork to ("" & (path to application "Play MiniTunes") & "Contents:Resources:" & "art.pict") as file specification
	set tempartwork1 to ("Macintosh HD:" & (path to application "Play MiniTunes") & "Contents:Resources:" & "art.pict" as string)
	try
		set theposixpath to (POSIX path of tempartwork1)
		do shell script "rm " & theposixpath
	end try
	set file_reference to (open for access tempartwork with write permission)
	try
		set eof file_reference to 512
		write art_data to file_reference starting at 513
		close access file_reference
	on error err
		close access file_reference
		error err
	end try
	
	tell window "mainWindow"
		set the contents of text field "title_text" of tab view item "controller_tab" of tab view "my_tab" to cur_song as string
		set the contents of text field "artist_text" of tab view item "controller_tab" of tab view "my_tab" to cur_artist as string
		set the contents of text field "album_text" of tab view item "controller_tab" of tab view "my_tab" to cur_album as string
		set image of image view "artwork" of tab view item "controller_tab" of tab view "my_tab" to load image "art.pict"
	end tell
end tracktext

I think this should work:

	tell window "mainWindow"
		set the contents of text field "title_text" of tab view item "controller_tab" of tab view "my_tab" to cur_song as string
		set the contents of text field "artist_text" of tab view item "controller_tab" of tab view "my_tab" to cur_artist as string
		set the contents of text field "album_text" of tab view item "controller_tab" of tab view "my_tab" to cur_album as string
		try
			delete (image of image view "artwork" of tab view item "controller_tab" of tab view "my_tab")
		end try
		set image of image view "artwork" of tab view item "controller_tab" of tab view "my_tab" to load image "art.pict"
		update
	end tell

awesome! works perfect!!
Thanks Dominik:D

Yuck, writing to temp files for image loading is ugly.:stuck_out_tongue: Using temporary files like this is not only asking for conflicts, it can be slow depending on existing system/finder load. Here’s a method that derives the image directly from the data in an obj-c method, so you don’t have to kludge things up with writing to temp files…

property imageView : ""

on awake from nib theObject --> Connect to "artwork" image view
	if name of theObject is "artwork" then
		set imageView to theObject
	end if
end awake from nib

on tracktext()
	using terms from application "iTunes"
		tell application "iTunes"
			if exists (database ID of current track) then
				set cur_song to (name of the current track as string)
				set cur_artist to (artist of the current track as string)
				set cur_album to (album of the current track as string)
				
				if (count artwork of current track) > 0 then
					set cur_art to data of artwork 1 of the current track as picture
				else
					set cur_art to missing value
				end if
			else
				set cur_song to "No song"
				set cur_artist to "currently"
				set cur_album to "playing"
				set cur_art to missing value
			end if
		end tell
	end using terms from
		
	(* Update the Text Fields *)
	tell tab view item "controller_tab" of tab view "my_tab" of window "mainWindow"
		set content of text field "title_text" to cur_song
		set content of text field "artist_text" to cur_artist
		set content of text field "album_text" to cur_album
	end tell

	call method "displayArtworkInImageView:trackHasArtwork:" of class "ArtworkController" with parameters {imageView, (cur_art is not missing value)}
end tracktext

Create a NSObject subclass… naming it “ArtworkController”… and placing the following code in the .h and .m files respectively.

ArtworkController.h…

ArtworkController.m…

Notice at the end of the .m file where it sets the image to an image named “NoArtwork.png”. This line inserts that default image into the image view when there’s no artwork for the current track. To make it simply delete the image and display no image, use the “image = nil;” line instead.

This should clean things up a lot for you, and you can get rid of that temp file workaround. There’s almost always a way to get out of using temporary files to create data and content representations. Sometimes there’s a creative way using just applescript, but oftentimes you’ll have to resort to using obj-c.

Hope that helps,
j

ok, I havent tried Jobu’s method yet, but Dominik’s worked great until I downloaded iTunes 7. Will Jobu’s script work in iTunes 7?

It works for me in 7. :cool:

j

im getting an NSInternalScriptError(8)
Not sure what causes it

I know jack-all when it comes to Obj-C so I don’t know where the error is coming from. All I know is the app will just close after the error is shown.

This is the log of when it tries to run and quits when the error comes up

Not sure what it means but maybe someone else can decipher it

Are you using a default image to display a “No image available”-style warning in your image view when there is no artwork? If so, make sure that you have added the image file to your project, and that it is named the same as the image listed on the obj-c code line…

If you do not want to use a default image, then comment out the above line (using “//” before the line), and then uncomment the line a few lines down so that reads…

This will simply delete any existing image in the image view when there is no artwork.

If this is not the problem then make sure you are implementing the applescript code correctly…

  1. You must verify in your applescript that there actually is artwork to read from. If you try to read artwork that isn’t there you’ll get an error. That’s why you see “set cur_art to missing value” in a few places throughout the code and why we send a boolean value to our obj-c method telling the method whether to even bother reading the data.

  2. Make sure that you have a valid, retained reference to your image view saved in a property variable. The property imageView : “” line and the initialization of the reference in the ‘awake from nib’ handler are very important. If you don’t send a valid reference to the obj-c method, it will of course not know what you are trying to set the image of.

If none of these clears things up, please just send me a copy of your project and I’ll work it out for you.

j

yeah i used the image-nil; thingie. I think all my Applescript is correct…I’m going to send you the source…se if you can fix it:D

Hello, hendo.

I took a look at your source code, and found a small handful of problems that are all compounding why your app is not working. The biggest problem in your code lies in how objects are initialized when the app is launched. Here are the steps I took to get your app working…

  1. You didn’t actually hook up your image view to the ‘awake from nib’ handler, you just added the code to the script. While this is a common mistake, you probably should have caught this when cut&pasting the code, as I clearly made note of the fact that you needed to connect it in my sample code…
on awake from nib theObject --> Connect to "artwork" image view

Try to pay close attention to comments contributors put into the code, as they often represent more than just notes. This was ultimately what was causing the specific error you were getting. In your declaration of the property (property imageView : “”) you were setting it to a string by default (you could also use ‘property imageView : missing value’). Since you never actually called the awake from nib handler code that was supposed to be attached to the image view, when you called the obj-c method it sent the empty string to the method rather than a reference to the image view.

As a side note, you should clean up your awake from nib handler a bit. In your existing implementation, you test for the image view and then jumble a bunch of code into the handler without testing for which object is calling it. Because you don’t test to see who’s executing the code, it’s executed every time the handler is called (which is why you see the error posted twice in the log… because two objects are connected to the handler. ;))

Your old code…

on awake from nib theObject
	if name of theObject is "artwork" then
		set imageView to theObject
	end if
	using terms from application "iTunes"
		tell application "iTunes"
			set sound_volume to sound volume
		end tell
	end using terms from
	tracktext()
	tell application "Finder" to set the visible of every process whose name is "iTunes" to false
end awake from nib

… and a cleaner version with my changes…

on awake from nib theObject
	if name of theObject is "artwork" then
		set imageView to theObject
	else if name of theObject is "mainWindow"
		using terms from application "iTunes"
			tell application "iTunes"
				set sound_volume to sound volume
			end tell
		end using terms from
		tell application "Finder" to set the visible of every process whose name is "iTunes" to false
	end if
end awake from nib
  1. You probably noticed in the modified code above, that I removed the call to the “tracktext()” subroutine. This is ultimately the bit of code that fired which caused the error. If you simply remove it (assuming you’ve resolved problem 1 above) everything works fine. The reason this is a problem, can be found here. What’s happening, is that everything in an application… especially at launch-time… happens in a specific order. Not only are certain events called in a set order, but objects are unarchived in a certain order. Objects are unarchived according to their view hierarchy. In your case, it is uber important to recognize that your window’s awake from nib handler is called before that of the image view. Because of this, when your window calls the handler, subsequently firing the ‘tracktext()’ subroutine, there is not yet a valid reference made to the image view. Once you remove the call to the ‘tracktext()’ subroutine, the tracktext() code isn’t called until AFTER the image view’s awake from nib handler is called, allowing it to properly initialize that reference. Don’t worry, we can still call your tracktext() sub to get a good initialization of the window, but we’ll have to wait just a bit longer before we do that. :cool:

  2. I noticed in your code that the window never opens, so I used the ‘on launched’ handler to add a call to open the window. Note in the doc that the FIle’s Owner’s ‘on launched’ handler fires after all of the awake from nib handlers have fired. This is a good place to do all of your initializations, because unlike jumbling init code in the awake from nib handler (where it may be called multiple times) the launched handler only fires once. This way, you can do any initilization of the window or other app configuration before opening the main window. I prefer this to making the window “Visible at launch time” in IB.

on launched theObject
	show window "mainWindow"
end launched
  1. I noticed in your code that you at one point had the ‘on opened’ handler connected to a window. This is the place where we execute our tracktext() subroutine that we removed from the awake from nib handler. By placing it in this handler instead, it refreshes our display everytime the user opens the window. I actually prefer to use the “will open” handler instead, that way if the window does need to be refreshed when it opens, it does so BEFORE it becomes visible, so there’s no flash of the old display before the new one is shown.
on opened theObject
	if name of theObject is "mainWindow" then
		tracktext()
	end if
end opened

Clean up your code a bit and make these changes, and you’ll be cruisin’.

Good luck,
j

awesome! thanks a ton Jobu, as usual you find the solution:D Your time is very appreciated!!

actually your code did something funny…the fast forward/skip track and rewind/backtrack buttons no longer work…weird…oh well hopefully i will be able to figure this out myself:D

ok i’m not sure why it isn’t working anymore…i thought maybe i just forgot to name the buttons in IB or forgot to check something off but it’s all there. I don’t know why Jobu’s code would mess that up because it doesn’t really connect at all.

This is my fast forward/skip track, rewind/back track code.

on mouse down theObject event theEvent
	global StartTimer, DelayTime
	set DelayTime to 0.2
	set StartTimer to time of (current date)
	if name of theObject is equal to "next_button" then tell application "iTunes" to fast forward
	if name of theObject is equal to "prev_button" then tell application "iTunes" to rewind
end mouse down

on mouse up theObject event theEvent
	global StartTimer, DelayTime
	set RFButtons to {"next_button", "prev_button"}
	set TimeDiff to (time of (current date)) - StartTimer
	if TimeDiff is greater than DelayTime and RFButtons contains (name of theObject) then
		tell application "iTunes" to resume
	else if name of theObject is equal to "next_button" then
		tell application "iTunes" to next track
	else if name of theObject is equal to "prev_button" then
		tell application "iTunes" to back track
	end if
end mouse up

NEVER MIND!!!
I fixed it
I accidentally had the buttons checked off as “on clicked” in IB when they should have been on mousedown/up. lol…yet another careless mistake by me:P

I got Jubu’s method to work and it works great, it just dosent update when the song changes. I was wondering if anybody knew how to fix that?

Model: Dual 1Ghz MDD
Browser: Firefox 2.0.0.3
Operating System: Mac OS X (10.4)

Adamj12b, check out this thread… j

Sorry for bringing up an old thread here but I’m having problems displaying the artwork in my new program.
It is in an image view called “art” in a tab view item called “info” in a tab view called “tabview”
This is what I have:

property imageView : ""
on awake from nib theObject
	if name of theObject is "art" then
		
		set imageView to theObject
		
	else if name of theObject is "window" then
		using terms from application "iTunes"
			tell application "iTunes"
				set sound_volume to sound volume
			end tell
			
			tell application "iTunes"
				set isPlaying to player state is playing
			end tell
		end using terms from
		set state of button "play" of tab view item "control" of tab view "tabview" of window "window" to isPlaying
		set state of button "shuffle" of tab view item "control" of tab view "tabview" of window "window" to iTunesShuffleState()
	end if

This is my track info subroutine

using terms from application "iTunes"
		tell application "iTunes"
			tell current track to if exists (database ID) then
				set {thesong, theartist, thealbum, database_ID} to {name as string, artist as string, album as string, database ID}
				if (count artwork) > 0 then
					set cur_art to data of artwork 1 as picture
				else
					set cur_art to missing value
				end if
				
			else
				set {thesong, theartist, thealbum, cur_art, database_ID} to {"No Song", "Currently", "Playing", missing value, missing value}
			end if
		end tell
	end using terms from
	tell window "window"
		set content of text field "title1" of tab view item "search" of tab view "tabview" to thesong
		set content of text field "artist1" of tab view item "search" of tab view "tabview" to theartist
		set content of text field "album1" of tab view item "search" of tab view "tabview" to thealbum
		try
			tell application "iTunes"
				set pp to player position
				set tl to duration of current track
			end tell
			set content of control "li1" of tab view item "search" of tab view "tabview" to (pp / tl)
		on error
			set content of control "li1" of tab view item "search" of tab view "tabview" to 0
		end try
		
	end tell
	tell window "window"
		set content of text field "title" of tab view item "control" of tab view "tabview" to thesong
		set content of text field "artist" of tab view item "control" of tab view "tabview" to theartist
		set content of text field "album" of tab view item "control" of tab view "tabview" to thealbum
		try
			tell application "iTunes"
				set pp to player position
				set tl to duration of current track
			end tell
			set content of control "li" of tab view item "control" of tab view "tabview" to (pp / tl)
		on error
			set content of control "li" of tab view item "control" of tab view "tabview" to 0
		end try
		
	end tell
	
	call method "displayArtworkInImageView:trackHasArtwork:" of class "ArtworkController" with parameters {imageView, (cur_art is not missing value)}
end tracktext

In my image view I have on awake from nib checked off, its all connected. I call my tracktext() method in an on idle handler, and in a on will become active handler. I noticed in IB there is no longer an “on opened” handler, but an “on open” handler…are they the same thing? Should I use that instead of on will become active? Oh and this is the error I’m getting.

Hi Jobu,

thank you very much for this obj-c “subroutine”. I tried it and it worked at first go (iTunes 7).

mine doesn’t…the only difference between when i used and and it worked and when it didn’t work is, in my old one (where it worked correctly) it was in a seperate panel. now its in a tab view item called “info” in a tab view named “tabview” in a window named “window”…and it now does nt work for some reason.