DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Scarf_song_titles.py

04.21.2010
| 2985 views |
  • submit to reddit
        This prints a list of songs from the iTunes db based on search terms
that you place inside the program for each run.  Crude, I know...

Works fine, though.

Requires python3.1

#!/usr/bin/env python3.1
# scarf_song_titles.py
# rev 1 (rev 0 lives at http://snippets.dzone.com/user/paul618)
# written just for fun, by xlforum.net/paul618 AKA L3P7ON

# PM me on xlforum.net with bugs or suggestions

# Q:: Isn"t this cheating?
# A:: Not if everybody was using it.

# This runs on python3.1 on any platform you can imagine.
# Tested on a Mac (v. 10.6.3)
# the paths defined in this section might be different on a PC or Gnu/Linux

# To run this on a Mac, Gnu/Linux or PC:
# 0) uncomment the  two  lines below the WARNING.
#    this gives the program PERMISSION to read your db and write a datafile

# 1) download python3.1
# 2) paste this program into a file called scarf_song_titles.py
# 3) fire up IDLE at Applications/Python 3.1/IDLE
# 4) File/Open scarf_song_titles.py
# 5) put your search criteria into two spots indicated
# 5) hit F5 to Run
# 6) click in the IDLE and goto step 5

# IMPORTANT:
# USE AT YOUR OWN RISK! The author does not warrent the reliability of this program.

# IFF you will allow this program to read your iTunes_Music_Library.xml
# fix the path and uncomment the next line...
#XMLfile = "/Users/paul/Music/iTunes/*.xml"

# I have to create an ASCII-only version of that file to avoid a program error.

# IFF you will allow this program to create such a file
# fix the path and uncomment the next line...
#datafile = "/Users/paul/song_title_game_data.xml"

# remove this file after you have installed some new tunes
# so that my datafile will be re-created

# --------------------------------------------------------------------------------------------

searchstring0 = "Red"    # PUT YOUR FIRST  SEARCHSTRING HERE BEFORE EACH RUN; hit command-s F5
searchstring1 = "Rain"   # PUT YOUR SECOND SEARCHSTRING HERE BEFORE EACH RUN; hit command-s F5

# --------------------------------------------------------------------------------------------


import re, os, sys, stat, time, struct
import glob

# you can subsitiute your own favs here
Amazons = ("Joni M", "Emmylou", "Rickie ", "Natalie", "Avril L", "Alison", "Gillian", "Cindi L"
           "Bonnie R", "Flora P", "Gillian", "Maura M", "Lucinda", "Sheryl C", "Alanis", "Patti S")


# ---- subrs

def uniqify(items):
    seen = set()
    ret = []
    for i in items:
        if not i in seen:
            ret.append(i)
            seen.add(i)
    return ret

def cleanline(L):
    L = L.replace("&", "and")
    i = L.rindex("<string>")
    return L[i+8:-10]   # trimmed


def report(data):    
    d = uniqify(data)
    for s, t in d:
        # bug may not support multiple ppl doin the same song...
        print(" %s -- %s" % (s, t))
        #print("site:lyrics.filestube.com %s -- %s" % (s, t))

# ---- end subr



# datafile will be the iTunes XML file cleaned of non-ASCII chars
# this is only created once, at the path defined in the preamble

if not os.path.exists(datafile):
    # datafile has not been created yet
    fn = glob.glob(XMLfile)[0]
    if 1: print("fn = >>>%s<<<" % (fn))
    buffersize = os.stat(fn)[stat.ST_SIZE]
    print("Your XML db is %d bytes.\nCreating %s.\nProcessing 160 songs per second...." % (buffersize, datafile))

    buffersize *= 1.1  # do I need this?
    buffersize = int(buffersize)
    
    # read
    with open(fn, 'rb', buffersize) as fh0: 
        red_data = fh0.read()
    fh0.close()

    # write
    fh1 = open(datafile, "wb")
    for byte in red_data:
        # delete non-ASCII chars
        if -128 <= byte <= 127:
            c = struct.pack('b', byte)
        fh1.write(c)
    fh1.close()

    time.sleep(.05)  # let it close



# ---- main

dolls = []
guys = []
title = "" # need this binding here
toggle = True

pattern0  = "<key>Name</key>"
pattern1  = "<key>Artist</key>"
q_pattern = "<key>Playlists</key>"

print("Searching %s. This will take a minute..." % (datafile))

file = open(datafile, "r")
for line in file.readlines():
    
    mo2 = re.search(searchstring0, line)   #, flags=re.IGNORECASE)  you want it to match case
    mo3 = re.search(searchstring1, line)   #, flags=re.IGNORECASE)

    mo0 = re.search(pattern0, line)   # Title
    mo1 = re.search(pattern1, line)   # Artist
    mo4 = re.search(q_pattern, line)  # quit before examining Playlists

    if mo4:
        continue  # lookin at Playlists, which are past the data of interest
        
    if mo0 and (mo2 or mo3):
        # collect song title
        title = cleanline(line)
        toggle = True  # prepare to print the Artist
        
    if mo1 and toggle:
        if 0: print("I see an Artist for %s" % (title))
        # collect artist
        artist = cleanline(line)
        if 0: print(" %s --> %s " % (title, artist))
        
        flag = False
        for gf in Amazons:
            if artist.startswith(gf):
                if 0: print("I see an Amazon: %s -- %s" % (title, artist))
                dolls.append((title, artist))
                if 0: print("I see somebody: %s -- %s" % (title, artist))
        # for/else!!!!!!!!!!!!!!  my first one ever. woohoo!!!!!!!!!!!!!!
        else:
            guys.append((title, artist))

        toggle = False

print("----")
print("---- Amazons:")
print("----")
dolls.sort()
report(dolls)

print("----")
print("---- everybody else:")
print("----")
guys.sort()
report(guys)
print("---- done.")

# ---- 
# ----

mail = '''
> 
> How do I write binary data to a file?
> I can open a file with 'wb' as mode but if I do:
> 
> b = open('bin.dat','wb')
> for i in range(50): b.write(i)
> 
> I get an error.

This is an error because the write method can only output a string.
Strings in Python are not strictly 7-bit characters, but 8-bit bytes.
So you can use the struct module to convert Python data into binary
strings, which can then be written to the file.

>>> import struct
>>> binfile = open('bin.dat', 'wb')
>>> for num in range(50):
...   data = struct.pack('i', num)
...   binfile.write(data)
...
>>> binfile = open('bin.dat', 'rb')
>>> intsize = struct.calcsize('i')
>>> while 1:
>>>   data = binfile.read(intsize)
...   if data == '':
...     break
...   num = struct.unpack('i', data)
...   print num
'''

typical_record = '''
                <key>2056</key>
                <dict>
                        <key>Track ID</key><integer>2056</integer>
                        <key>Name</key><string>Ornithology</string>
                        <key>Artist</key><string>Charlie Parker</string>
                        <key>Composer</key><string>Parker, C. - Harris, B.</string>
                        <key>Album</key><string>Ken Burns Jazz</string>
                        <key>Genre</key><string>Jazz</string>
                        <key>Kind</key><string>AAC audio file</string>
                        <key>Size</key><integer>2892331</integer>
                        <key>Total Time</key><integer>181026</integer>
                        <key>Track Number</key><integer>7</integer>
                        <key>Track Count</key><integer>16</integer>
                        <key>Year</key><integer>2000</integer>
                        <key>Date Modified</key><date>2008-06-06T20:07:06Z</date>
                        <key>Date Added</key><date>2007-03-01T21:59:06Z</date>
                        <key>Bit Rate</key><integer>128</integer>
                        <key>Sample Rate</key><integer>44100</integer>
                        <key>Play Count</key><integer>3</integer>
                        <key>Play Date</key><integer>3350641115</integer>
                        <key>Play Date UTC</key><date>2010-03-05T17:38:35Z</date>
                        <key>Persistent ID</key><string>4BA3750063A2DDFC</string>
                        <key>Track Type</key><string>File</string>
                        <key>File Type</key><integer>1295270176</integer>
                        <key>Location</key><string>file://localhost/Users/paul/Music/iTunes/iTunes%20Music/Unknown%20Artist/Unknown%20Album/07%20Track%2007.m4a</string>
                        <key>File Folder Count</key><integer>4</integer>
                        <key>Library Folder Count</key><integer>1</integer>
                </dict>
'''