Aubin, Dischi, and anyone else interested in album covers,


Below is some correspondence between a friend (Brad Spencer) and I about support for per-mp3 covers and related things. He sent me a few patches, the latest of which is attached. You guys are more into this part of the code so please review / change / apply. I think there are also some bugfixes.

Please read below, it is our emails in chronological order. This patch should basicly fix some COVER_DIR handling and add support for per-mp3 covers.

-Rob

--------

Here's a patch to the current CVS.  It changes coversearch so if the
normal spot for coversearch covers is readonly (or has another error),
it tries to use the FREEVO_CACHEDIR instead.  Changes in autoitem
allow it to look there too, and tada!  CD- or DVD-based MP3s and OGGs
can have covers found via coversearch.  And so can my readonly
samba-mounts   And they stay across loads as long as the cache is
not blown away.

BTW, say you have an album stored like so:

/dos/smegma/d/mp3/Music/Chet/*.mp3 # all songs on the Chet album

Then the cover ends up in

$FREEVO_CACHEDIR/dos/smegma/d/mp3/Music/Chet/cover.jpg

i.e. the same place as it would without this patch, except with the
$FREEVO_CACHEDIR thrown on the front.  This approach has worked well
for me with photos.jacknife.org's thumbnails..

-----------

This one's way smarter and probably has less bugs.  It also allows
per-item album covers to be saved (they were already supported in
audioitem, but I made them work from the cache and coversearch as
well).

Also, the covers are now in $FREEVO_CACHEDIR/covers/...

Please make it smarter or throw it away as you see fit :)

------------

On Sun, Oct 12, 2003 at 07:46:55PM -0300, Rob Shortt wrote:

>>
>> Have you checked out the COVER_DIR config item?  Doesn't that eliminate
>> the need for the cover dir in the cache (which could get hosed btw).


It could, if it were re-organized. It looks like not everything knows about COVER_DIR, and those that do only look in $COVER_DIR/$ALBUM_NAME.jpg, which is kinda silly if you ask me. But, I could easily change it to use COVER_DIR instead.

What I've actually done is used freevo to build the covers and then I
copied them back out of the cache.  Putting them directly in COVER_DIR
might be a better move..

-----------

Ok, I put everything in COVERS_DIR and fixed the damn image display.
It was way wack, trying to create Image objects (but the _wrong_ Image
objects, not gui.Image) and pass them to MenuItems.  But that can't
work; MenuItem needs filenames, and it made sense to download them
just _once_ per menu display anyway (!) so I put them in
$FREEVO_CACHEDIR/coversearch.

But then it looks like mmpython is hosed and doesn't know when a file
is updated and keeps displaying the old version of the image.  So I
had to make the files have unique parts in them.

On the way, I noticed that the code in gui/Image.py is totally fucked
and can't possibly work, if I understand python at all (it calls the
base class contructor _after_ messing with its data, so the messing
with is forgotten).

Oh, and games/__init__.py constantly pops up "please update DIR_GAMES
in local_conf.py" when I do a recursive random playlist.  I don't have
any of the game stuff setup, so I commented out the popup box for now


And when does all that data in mmpython (and worse, in thumbnails) get cleaned up?

Python is very cool, except the "runtime member binding" means it
takes me about 100 times longer to make a change 'cause the compiler
can't catch my typos and mistakes

I didn't test every single pathway through this code, but it seems to
work...

--------------

On Mon, Oct 13, 2003 at 08:10:49AM -0300, Rob Shortt wrote:

>> LOL, it's not supposed to work yet. Nothing else uses it... hehehe.


Hahahahahahaha! Yeah, at first I played with it to see if it I could make it work the way that coversearch.py was trying to use it but this was just plain easier.


>> That's wierd, do you have an old style DIR_GAMES in your local_conf.py >> anyways?


No... None at all. That's the weird part.



>> I'm going to forward your stuff to the mailing list, unless you want to,
>> so Aubin and Dischi can take a look at it. Dischi's also a developer of
>> mmpython so maybe he can fix any problems with it or give some insight.



Forward away. My point was for you to kinda say "you're insane" or "maybe that's okay". I only intended to change like 5 lines of code when I started, but then I was like "Oh! That's broken too!"

Anyway, it works great for me   The code can easily be rearranged to
put COVER_DIR ahead of the media directories themselves in the "search
order" if necessary.

-------------

Index: audioitem.py
===================================================================
RCS file: /cvsroot/freevo/freevo/src/audio/audioitem.py,v
retrieving revision 1.38
diff -u -p -r1.38 audioitem.py
--- audioitem.py        21 Sep 2003 13:15:56 -0000      1.38
+++ audioitem.py        13 Oct 2003 02:59:01 -0000
@@ -107,7 +107,7 @@ class AudioItem(Item):
         # use the standard imghdr function to check if
         # it's a real png, and not a lying one :)
 
-        # Check for cover in COVER_DIR
+        # Check for simplistic $ALBUM.jpg cover in COVER_DIR
         if config.COVER_DIR:
             base = os.path.join(config.COVER_DIR, os.path.basename(file))
             self.image = util.getimage(base, self.image)
@@ -118,11 +118,22 @@ class AudioItem(Item):
         elif os.path.isfile(cover_logo+'jpg') and imghdr.what(cover_logo+'jpg'):
             self.image = cover_logo+'jpg'
 
-        # Allow per mp3 covers. As per Chris' request ;)
+        # Check for cover in the cache directory as if it were in this directory
+        coverdir = config.COVER_DIR + '/' 
+        if os.path.isfile(coverdir + cover_logo + 'png'):
+            self.image = coverdir + cover_logo + 'png'
+        elif os.path.isfile(coverdir + cover_logo + 'jpg'):
+            self.image = coverdir + cover_logo + 'jpg'
+         
+        # Allow per-item covers, considering the cache, too
         if os.path.isfile(os.path.splitext(file)[0] + '.png'):
             self.image = os.path.splitext(file)[0] + '.png'
         elif os.path.isfile(os.path.splitext(file)[0] + '.jpg'):
             self.image = os.path.splitext(file)[0] + '.jpg'
+        elif os.path.isfile(os.path.splitext(coverdir + file)[0] + '.png'):
+            self.image = os.path.splitext(coverdir + file)[0] + '.png'
+        elif os.path.isfile(os.path.splitext(coverdir + file)[0] + '.jpg'):
+            self.image = os.path.splitext(coverdir + file)[0] + '.jpg'
 
         # Let's try to find if there is any image in the current directory
         # that could be used as a cover
Index: plugins/coversearch.py
===================================================================
RCS file: /cvsroot/freevo/freevo/src/audio/plugins/coversearch.py,v
retrieving revision 1.19
diff -u -p -r1.19 coversearch.py
--- plugins/coversearch.py      10 Sep 2003 19:30:08 -0000      1.19
+++ plugins/coversearch.py      13 Oct 2003 02:59:01 -0000
@@ -92,8 +92,6 @@ import re
 import urllib2
 import time
 import config
-import Image
-import cStringIO
 from xml.dom import minidom # ParseError used by amazon module
 
 from gui.PopupBox import PopupBox
@@ -218,41 +216,79 @@ class PluginInterface(plugin.ItemPlugin)
             return
 
         items = []
+        per_items = []
+
+        # Where will we store the temporary images?  We clean up the directory
+        # each time we use it so that (a) we don't have to hook to cleanup on
+        # exit, and (b) to workaround mmpython not knowing when these files
+        # have changed.  The mmpython thing looks like a bug.
+        image_dir = config.FREEVO_CACHEDIR + '/coversearch/'
+        if os.access(image_dir, os.W_OK) == 0:
+            os.makedirs(image_dir, 0755)
+        for file in os.listdir(image_dir):
+            os.unlink(os.path.join(image_dir, file))
         
         # Check if they're valid before presenting the list to the user
         # Grrr I wish Amazon wouldn't return an empty gif (807b)
 
         for i in range(len(cover)):
+            found = False
+            label = ''
+
+            # Where will we store the temporary image?  Make the name "sort of unique"
+            # so mmpython doesn't think it's another file.
+            image_file = image='%s/img%u.%s.jpg' % (image_dir, i, cover[i].Asin)
+                
             m = urllib2.urlopen(cover[i].ImageUrlLarge)
             if not (m.info()['Content-Length'] == '807'):
-                image = Image.open(cStringIO.StringIO(m.read()))
-                items += [ menu.MenuItem('%s' % cover[i].ProductName,
-                                         self.cover_create, cover[i].ImageUrlLarge,
-                                         image=image) ]
-                m.close()
-            else:
+                found = True
+                
+            if not found:
                 m.close()
                 # see if a small one is available
-                n = urllib2.urlopen(cover[i].ImageUrlMedium)
-                if not (n.info()['Content-Length'] == '807'):
-                    image = Image.open(cStringIO.StringIO(n.read()))
-                    items += [ menu.MenuItem('%s [small]' % cover[i].ProductName,
-                                    self.cover_create, cover[i].ImageUrlMedium) ]
-                    n.close()
-                else:
-                    n.close()
-                    # maybe the url is wrong, try to change '.01.' to '.03.'
-                    cover[i].ImageUrlLarge = cover[i].ImageUrlLarge.replace('.01.', 
'.03.')
-                    n = urllib2.urlopen(cover[i].ImageUrlLarge)
-                    if not (n.info()['Content-Length'] == '807'):
-                        image = Image.open(cStringIO.StringIO(n.read()))
-                        items += [ menu.MenuItem('%s [small]' % cover[i].ProductName,
-                                                 self.cover_create, 
cover[i].ImageUrlLarge) ]
-                    n.close()
+                m = urllib2.urlopen(cover[i].ImageUrlMedium)
+                if not (m.info()['Content-Length'] == '807'):
+                    found = True
+                    label = ' [small]'
+
+            if not found:                                            
+                m.close()
+                # maybe the url is wrong, try to change '.01.' to '.03.'
+                cover[i].ImageUrlLarge = cover[i].ImageUrlLarge.replace('.01.', 
'.03.')
+                m = urllib2.urlopen(cover[i].ImageUrlLarge)
+                if not (m.info()['Content-Length'] == '807'):
+                    found = True
+
+            if found:                                                     
+                # If we found a usable image URL, download it to a temporary file
+                try:
+                    os.unlink(image_file)
+                except:
+                    pass
+                    
+                fp = open(image_file, 'wb')
+                fp.write(m.read())
+                fp.close()
+
+                # Is it real?
+                items += [ menu.MenuItem('%s%s' % (cover[i].ProductName, label),
+                                         self.cover_create, [image_file, 0],
+                                         image=image_file) ]
+                if config.COVERSEARCH_PER_ITEM:
+                    per_items += [ menu.MenuItem('[per-item] %s%s' % 
(cover[i].ProductName, label),
+                                                 self.cover_create,
+                                                 [image_file, 1], image=image_file) ]
+                                                     
+            # Close the file regardless
+            m.close()
 
         box.destroy()
+        
+        # Add any per-item entries to the menu.  There may be none.
+        items += per_items
+
         if len(items) == 1:
-            self.cover_create(arg=items[0].arg, menuw=menuw)
+            self.cover_create(arg=[items[0].arg, 0], menuw=menuw)
             return
         if items: 
             moviemenu = menu.Menu('Cover Results', items)
@@ -272,19 +308,40 @@ class PluginInterface(plugin.ItemPlugin)
         """
         import amazon
         import directory
-        
-        box = PopupBox(text='getting data...')
+
+        is_per_item = arg[1]
+        popup_text = 'Downloading and saving cover'
+        if is_per_item:
+            popup_text += ' (per item)'
+        box = PopupBox(text=popup_text)
         box.show()
         
-        #filename = os.path.splitext(self.item.filename)[0]
         if self.item.type == 'audiocd':
             filename = '%s/mmpython/disc/%s.jpg' % (config.FREEVO_CACHEDIR,
                                                     self.item.info['id'])
         else:
-            filename = '%s/cover.jpg' % (os.path.dirname(self.item.filename))
+            # Apply this cover per-item?
+            if not is_per_item:
+                # No, just a directory cover
+                filename = '%s/cover.jpg' % (os.path.dirname(self.item.filename))
+            else:
+                filename = os.path.splitext(self.item.filename)[0] + '.jpg'
+
+        # Try to open the file for writing
+        try:        
+            m = open(filename,'wb')
+        except IOError:
+            if not config.COVER_DIR:
+                raise
+            # Try using the COVER_DIR cache instead; maybe the item's directory is 
read-only
+            coverdir = config.COVER_DIR + '/' + os.path.dirname(self.item.filename)
+            if os.access(coverdir, os.W_OK) == 0:
+                os.makedirs(coverdir, 0775)
+            filename = config.COVER_DIR + '/' + filename
+            m = open(filename,'wb')
 
-        fp = urllib2.urlopen(str(arg))
-        m = open(filename,'wb')
+        # Save the cover image to that filename
+        fp = open(arg[0], 'rb')
         m.write(fp.read())
         m.close()
         fp.close()
@@ -292,7 +349,7 @@ class PluginInterface(plugin.ItemPlugin)
         if self.item.type == 'audiocd':
             self.item.image = filename
             
-        if not self.item.type == 'audiocd' and self.item.parent.type == 'dir':
+        if not self.item.type == 'audiocd' and self.item.parent.type == 'dir' and not 
is_per_item:
             # set the new cover to all items
             self.item.parent.image = filename
             for i in self.item.parent.menu.choices:

Reply via email to