Last.fm stuff, insert rows in library view

git-svn-id: file:///home/anna/Desktop/ocarina-legacy/mithos/ocarina@45 1daee41c-8060-4895-b1f0-2197c00d777a
This commit is contained in:
bjschuma 2009-07-13 03:42:43 +00:00
parent 57393a9150
commit 5b752c95e8
11 changed files with 352 additions and 125 deletions

View File

@ -1,15 +1,26 @@
open:
geany src/GuiObjects/*.py &
geany &
geany src/GuiObjects/*.py
geany src/*.py
clean:
rm -rf src/*.pyo
rm -rf src/*~
rm -rf src/*.c
rm -rf src/*.o
rm -rf src/*.so
rm -rf src/GuiObjects/*.pyo
rm -rf src/GuiObjects/*~
rm -rf src/GuiObjects/*.c
rm -rf src/GuiObjects/*.o
rm -rf src/GuiObjects/*.so
rm *~
compile:
cd src && ./cythonize
cd src/GuiObjects && ./cythonize
install:
# mkdir ~/bin/ocarina-bin
rsync -avz src/ ~/bin/ocarina-bin/src

View File

@ -16,9 +16,9 @@ class ControlPanel(gtk.HBox):
self.next = plist.next
self.pack_start(CheckButton("Random",self.toggleRand,self.data.random),False,False,0)
self.makeProgressBar()
(self.nextImg,self.nextBtn) = self.makeButton("next","next.png",None,self.next)
(self.stopImg,self.stopBtn) = self.makeButton("stop","stop.png",None,self.stop)
(self.playImg,self.plauseBtn) = self.makeButton("plause","play.png",None,self.plause)
(self.stopImg,self.stopBtn) = self.makeButton("stop","stop.png",None,self.stop)
(self.nextImg,self.nextBtn) = self.makeButton("next","next.png",None,self.next)
self.show()
@ -28,7 +28,8 @@ class ControlPanel(gtk.HBox):
if img:
image = Image(os.path.join("images",img))
button = Button(name,image,text,func)
self.pack_end(button,False,False,0)
#self.pack_end(button,False,False,0)
self.pack_start(button,False,False,0)
return (image,button)
@ -46,14 +47,13 @@ class ControlPanel(gtk.HBox):
event.show()
vbox.show()
pbar.show()
gobject.timeout_add(1000,self.updatePBar,pbar)
gobject.timeout_add(500,self.updatePBar,pbar)
def pbarclick(self,widgit,data,pbar):
if data.button == 1:
prcnt = float(data.x) / float(pbar.get_allocation()[2])
self.data.song.seek(int(prcnt * self.data.song.info.duration * 1000000000))
#print float(data.x)/float(pbar.get_allocation()[0])
# Play/Pause function
@ -78,15 +78,16 @@ class ControlPanel(gtk.HBox):
# Update time/progress of the progress bar
def updatePBar(self,pbar):
if self.data.song and (self.data.song.playing == False):
time = 0
if not self.data.song:
return True
try:
(success,time) = self.data.song.curTime()
time = self.data.song.curTime()
except:
success = False
if success == True:
time = 0
if self.data.song.info.duration > 0:
pbar.set_fraction(float(time)/self.data.song.info.duration)
pbar.set_text(self.data.song.info.fixTime(time) + " / " + self.data.song.info.length)
pbar.set_text(self.data.song.info.fixTime(time) + " / " + self.data.song.info.length)
return True

View File

@ -19,9 +19,9 @@ class InfoView(gtk.VBox):
album = ""
artist = ""
if self.data.song:
title = self.data.song.info.title.title()
album = "from "+self.data.song.info.album.title()
artist = "by "+self.data.song.info.artist.title()
title = self.data.song.info.title
album = "from "+self.data.song.info.album
artist = "by "+self.data.song.info.artist
self.title.set_text(title)
self.album.set_text(album)
self.artist.set_text(artist)

View File

@ -8,10 +8,12 @@ from menuItem import MenuItem
class LibView(gtk.VBox):
def __init__(self,data):
gtk.VBox.__init__(self,False,0)
data.library.libview = self
self.win = gtk.ScrolledWindow()
self.win.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
self.data = data
self.treeview = None
self.tree = gtk.TreeStore(str,int)
self.col = None
self.plist = None
self.pbar = gtk.ProgressBar()
@ -25,38 +27,36 @@ class LibView(gtk.VBox):
# Use to update the library pane
def update(self):
rval = self.data.library.scanning
if self.treeview:
self.win.remove(self.treeview)
self.treeview.remove_column(self.col)
self.pbar.pulse()
self.pbar.set_text("Found "+str(self.data.library.count)+" files.")
tree = gtk.TreeStore(str,int)
for artist in self.data.library.artAlb.keys():
ariter = tree.append(None,[artist.title(),-1])
for album in self.data.library.artAlb[artist]:
aliter = tree.append(ariter,[album.title(),-1])
for track in self.data.library.albTrk[(artist,album)]:
tree.append(aliter,[self.data.library.files[track].title.title(),self.data.library.files[track].id])
tree.set_sort_column_id(0,gtk.SORT_ASCENDING)
self.treeview = gtk.TreeView(tree)
self.treeview.set_enable_search(True)
#if self.treeview:
#self.win.remove(self.treeview)
#self.treeview.remove_column(self.col)
#self.tree = gtk.TreeStore(str,int)
#self.pbar.pulse()
#self.pbar.set_text("Found "+str(self.data.library.count)+" files.")
#for artist in self.data.library.artAlb.keys():
# ariter = self.tree.append(None,[artist,-1])
# for album in self.data.library.artAlb[artist]:
# aliter = self.tree.append(ariter,[album,-1])
# for track in self.data.library.albTrk[(artist,album)]:
# self.tree.append(aliter,[self.data.library.files[track].title,self.data.library.files[track].id])
self.tree.set_sort_column_id(0,gtk.SORT_ASCENDING)
self.treeview = gtk.TreeView(self.tree)
#self.treeview.set_enable_search(True)
self.treeview.connect("button_release_event",self.clicked)
self.col = gtk.TreeViewColumn('Library ('+str(self.data.library.count)+')')
self.treeview.append_column(self.col)
cell = gtk.CellRendererText()
self.col.pack_start(cell,True)
self.col.add_attribute(cell,'text',0)
self.col.set_sort_column_id(0)
#self.col.set_sort_column_id(0)
self.treeview.set_rules_hint(True)
self.treeview.show()
self.selection = self.treeview.get_selection()
self.selection.set_mode(gtk.SELECTION_MULTIPLE)
self.win.add(self.treeview)
self.updateLabel()
if rval==False:
self.pbar.hide()
return rval
for file in self.data.library.files:
self.insertSong(file)
#self.updateLabel()
def makeLabel(self):
@ -72,7 +72,22 @@ class LibView(gtk.VBox):
# Begin updating the library
def updates(self):
self.pbar.show()
gobject.timeout_add(1000,self.update)
self.updating = True
gobject.timeout_add(500,self.updateLibview)
def updateLibview(self):
self.pbar.set_text("Found "+str(self.data.library.count)+" files.")
self.pbar.pulse()
#self.updateLabel()
self.col.set_title('Library ('+str(self.data.library.count)+')')
return self.updating
def stopUpdates(self):
self.updating = False
self.pbar.hide()
self.data.dump()
# Right click menu
@ -88,6 +103,37 @@ class LibView(gtk.VBox):
self.rcmenu.popup(None,None,None,data.button,data.time)
def insertSong(self,song):
foundArt = False
foundAlb = False
artist = song.artist
album = song.album
track = song.title
ariter = self.tree.get_iter_root()
while ariter:
if self.tree[ariter][0]==artist:
foundArt = True
break
else:
ariter = self.tree.iter_next(ariter)
aliter = self.tree.iter_children(ariter)
while aliter:
if self.tree[aliter][0]==album:
foundAlb = True
break
else:
aliter = self.tree.iter_next(aliter)
if foundArt == False:
ariter = self.tree.insert(None,0,[artist,-1])
if foundAlb == False:
aliter = self.tree.insert(ariter,0,[album,-1])
self.tree.insert(aliter,0,[song.title.title(),song.id])
def populatePlaylist(self,widgit,func,data):
(model,pathlist) = self.selection.get_selected_rows()
list = []

View File

@ -112,7 +112,7 @@ class PlistView(gtk.Notebook):
i = len(dst)
for index in src:
track = self.data.library.files[index]
dst.insert(i,[track.id,track.title.title(),track.length,track.artist.title(),track.album.title(),track.count])
dst.insert(i,[track.id,track.title,track.length,track.artist,track.album,track.count])
time+=track.duration
i+=1
return time
@ -220,6 +220,7 @@ class PlistView(gtk.Notebook):
self.data.curSong = self.pfilter[iter][0]
self.loadSong(False)
self.controls.plause(None,None)
self.data.scrobbler.nowPlaying(self.data.library.files[self.data.curSong])
# Go to the next song in the list
@ -245,7 +246,7 @@ class PlistView(gtk.Notebook):
self.data.playingQ = True
self.setTabText("queue")
elif self.data.random == True:
self.data.curSong = self.pfilter[random.randint(0,len(self.pfilter))][0]
self.data.curSong = self.pfilter[random.randint(0,len(self.pfilter)-1)][0]
else:
next = self.pfilter[0][0]
for i in range(len(self.pfilter)-1):
@ -261,6 +262,7 @@ class PlistView(gtk.Notebook):
if pause == False:
self.controls.plause(None,None)
self.data.scrobbler.nowPlaying(self.data.library.files[self.data.curSong])
self.controls.changeImg()
self.status = CONTINUE
@ -270,6 +272,7 @@ class PlistView(gtk.Notebook):
if len(self.data.curList) == 0:
return
if self.data.song:
self.data.scrobbler.submit(self.data.song.timestamp)
self.data.song.close()
self.data.song = Song(self.data.library.files[self.data.curSong],self.next)
self.info.changeLabels()
@ -328,13 +331,27 @@ class PlistView(gtk.Notebook):
def hideRows(self,list,iter):
if self.search != "":
file = self.data.library.files[list[iter][0]]
if re.search(self.search,file.title):
if self.search == "":
return True
elif len(self.search) == 1:
try:
self.data.library.files[list[iter][0]].single[self.search]
return True
elif re.search(self.search,file.artist):
except:
return False
elif len(self.search) == 2:
try:
self.data.library.files[list[iter][0]].double[self.search]
return True
elif re.search(self.search,file.album):
return True
return False
return True
except:
return False
#if self.search in self.data.library.files[list[iter][0]].letters == True:
# return True
file = self.data.library.files[list[iter][0]]
if re.search(self.search,file.titlel):
return True
elif re.search(self.search,file.artistl):
return True
elif re.search(self.search,file.albuml):
return True
return False

View File

@ -1,10 +1,12 @@
import urllib
import urllib2
#import pygtk
#pygtk.require('2.0')
import gtk
import webbrowser
#import thread
import hashlib
import datetime
import time
from button import Button
@ -20,6 +22,12 @@ class Scrobbler(gtk.VBox):
self.key = "2c76f85a6704efd74b5a358821284ef9"
self.secret = "3a6012bfb627b60a884cf33fc044885c"
self.token = ""
# Session ID
self.session = ""
# Now playing URL
self.npurl = ""
# Submission URL
self.suburl = ""
# Run these in new thread so we can keep loading other things while waiting
self.makegui()
@ -30,66 +38,165 @@ class Scrobbler(gtk.VBox):
def makegui(self):
if self.data.lfm == "":
self.nosession = Button("lfm",None,"Click here to configure Ocarina for submitting songs to last.fm",self.setup)
self.nosession.set_size_request(100,100)
self.pack_start(self.nosession,False,False,0)
uname = gtk.Label("Username:")
username = gtk.Entry()
passwd = gtk.Label("Password:")
password = gtk.Entry()
password.set_visibility(False)
username.connect("activate",self.fetchSession,username,uname,password,passwd)
password.connect("activate",self.fetchSession,username,uname,password,passwd)
uname.show()
username.show()
passwd.show()
password.show()
self.pack_start(uname,False,False,0)
self.pack_start(username,False,False,0)
self.pack_start(passwd,False,False,0)
self.pack_start(password,False,False,0)
else:
self.handshake()
#self.labels = [gtk.Label("")]*10
self.similar = []
for i in range(10):
label = gtk.Label(" ")
label.show()
self.pack_start(label,False,False,0)
self.similar+=[label]
# One time setup (in theory)
def setup(self,widgit,data):
self.nosession.hide()
self.remove(self.nosession)
self.contBtn = Button("lfm",None,"Click to continue",self.fetchSession)
self.pack_start(self.contBtn,False,False,0)
self.fetchToken()
self.authorize()
# Fetch the authentication token
def fetchToken(self):
(url,list) = self.addMethod("auth.gettoken")
(url,list) = self.addParam(url,list,"api_key",self.key)
status = self.placeRequest(url)
attr = status.getAttributeNode("status")
if attr.value != "ok":
def fetchSession(self,widgit,username,uname,password,passwd):
self.data.lfmuser = username.get_text().lower()
self.data.lfmpass = self.md5(password.get_text())
if self.data.lfmuser=="" or self.data.lfmpass=="":
return
self.token += hashlib.md5(status.firstChild.nextSibling.firstChild.data).hexdigest()
else:
self.token = self.md5(self.data.lfmuser+self.data.lfmpass)
(url,list) = self.addMethod(self.url,"auth.getMobileSession")
(url,list) = self.addParam(url,list,"username",self.data.lfmuser)
(url,list) = self.addParam(url,list,"authToken",self.token)
(url,list) = self.addParam(url,list,"api_key",self.key)
(url,list) = self.addParam(url,list,"api_sig",self.getSig(list))
status = self.parseRequest(url)
attr = status.getAttributeNode("status")
if attr.value != "ok":
return
self.data.lfm = status.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.firstChild.data
username.hide()
uname.hide()
password.hide()
passwd.hide()
self.makegui()
# Open to authorization page
def authorize(self):
url = "http://www.last.fm/api/auth/?api_key="+self.key
#(url,list) = self.addParam(url,[],"api_key",self.key)
(url,list) = self.addParam(url,[],"token",self.token)
print url
#url+="&"+self.token
webbrowser.open(url)
def fetchSession(self,widgit,data):
(url,list) = self.addMethod("auth.getSession")
def handshake(self):
print "shaking hands"
url = "http://post.audioscrobbler.com/?hs=true"
list = [("hs","true")]
(url,list) = self.addParam(url,list,"p","1.2.1")
(url,list) = self.addParam(url,list,"c","tst")
(url,list) = self.addParam(url,list,"v","1.0")
(url,list) = self.addParam(url,list,"u",self.data.lfmuser)
tstp = self.timestamp()
(url,list) = self.addParam(url,list,"t",tstp)
(url,list) = self.addParam(url,list,"a",self.authToken(tstp))
(url,list) = self.addParam(url,list,"api_key",self.key)
(url,list) = self.addParam(url,list,"token",self.token)
sig = self.getSig(list)
(url,list) = self.addParam(url,None,"api_sig",sig)
print url
#status = self.placeRequest(url)
return
#attr = status.getAttributeNode("status")
#if attr.value != "ok":
# return
#node = status.firstChild.firstChild
#user = node.data
#key = node.nextSibling.data
#subscriber = node.nextSibling.nextSibling.data
#print user,key,subscriber
status = self.placeRequest(url).readlines()
if status[0].strip() != "OK":
return
self.session = status[1].strip()
self.npurl = status[2].strip()#+"/?"
self.suburl = status[3].strip()#+"/?"
#for line in status:
# print line.strip()
def nowPlaying(self,info):
vals = {"s":self.session,
"a":info.artist.title(),
"t":info.title.title(),
"b":info.album.title(),
"l":str(info.duration),
"n":info.tnum,
"m":""}
data = urllib.urlencode(vals)
#status = self.placeRequest(url).readlines()
req = urllib2.Request(self.npurl,data)
req.add_header("User-Agent","Ocarina")
status = urllib2.urlopen(req)
self.fetchSimilar(info.artist)
#if status[0].strip() == "OK":
# print "Submission successful"
def addMethod(self,method):
def submit(self,timestp):
info = self.data.song.info
if info.duration < 30:
return
exit = True
if (self.data.song.position > 240):
exit = False
if self.data.song.position > (self.data.song.info.duration/2):
exit = False
if exit == True:
return
vals = {"s":self.session,
"a[0]":info.artist.title(),
"t[0]":info.title.title(),
"i[0]":timestp,
"o[0]":"P",
"r[0]":"",
"l[0]":str(info.duration),
"b[0]":info.album.title(),
"n[0]":info.tnum,
"m[0]":""}
data = urllib.urlencode(vals)
req = urllib2.Request(self.suburl,data)
req.add_header("User-Agent","Ocarina")
status = urllib2.urlopen(req)
for line in status:
print line
#print status
def fetchSimilar(self,artist):
(url,list) = self.addMethod(self.url,"artist.getsimilar")
(url,list) = self.addParam(url,list,"artist",artist)
(url,list) = self.addParam(url,list,"limit","10")
(url,list) = self.addParam(url,list,"api_key",self.key)
status = self.parseRequest(url)
list = status.firstChild.nextSibling.childNodes
for i in range(list.length):
node = list.item(i)
if node.hasChildNodes()==True:
nameNode = node.firstChild.nextSibling.firstChild
self.similar[i/2].set_text(nameNode.data)
#for node in status.firstChild.childNodes:
# print node
def authToken(self,t):
return self.md5(self.data.lfmpass+t)
def md5(self,string):
return hashlib.md5(string).hexdigest()
def timestamp(self):
now = datetime.datetime.now()
return str(int(time.mktime(now.timetuple())))
def addMethod(self,url,method):
return (self.url+"?method="+method,[("method",method)])
def addParam(self,url,list,param,val):
val = val.replace(" ","%20")
url+="&"+param+"="+val
if list:
list += [(param,val)]
@ -102,19 +209,19 @@ class Scrobbler(gtk.VBox):
string = ""
for entry in list:
string+=entry[0]+entry[1]
string = string + self.secret
m = hashlib.md5(string)
return m.hexdigest()
def sendReq(self,req):
req+=self.secret
#hash = md5.new(req).digest()
#return self.placeRequest(self.url+"?"+hash)
#string = string + self.secret
return self.md5(string+self.secret)
#m = hashlib.md5(string)
#return m.hexdigest()
def placeRequest(self,url):
#print url
req = urllib2.Request(url)
req.add_header('User-Agent','Ocarina')
return minidom.parse(urllib2.urlopen(req)).documentElement
#return minidom.parse(urllib2.urlopen(req)).documentElement
return urllib2.urlopen(req)
def parseRequest(self,url):
return minidom.parse(self.placeRequest(url)).documentElement

View File

@ -9,24 +9,26 @@ import thread
class Library:
#def __init__(self,prnt):
def __init__(self):
self.goodTypes = ["ogg","mp3","wav","flac","mid"]#,"wma"]
self.goodTypes = ["ogg","mp3","flac"]#,"wma"]
self.reset()
self.scanning = False
#self.scanning = False
self.libview = None
#self.notAdded = open("/home/bjschuma/Desktop/notAdded.txt",'w')
# Begin a scan on dir
def scan(self,thread,dir):
self.scanning = True
#self.scanning = True
self.reset()
self.libview.updates()
self.path = os.path.expanduser(dir)
if os.path.exists(self.path) == False:
#print "Directory not found: %s" % dir
return
#print "Scanning: "+self.path
self.traverse("")
self.scanning = False
self.libview.stopUpdates()
#self.scanning = False
#print "Found %s files!" % str(self.count)
@ -58,6 +60,7 @@ class Library:
song.filename = os.path.join(self.path,joined)
self.files+=[song]
thread.start_new_thread(self.add,(self.hash(joined),self.count))
#self.add(self.hash(joined),self.count)
self.count += 1
#self.add(self.hash(joined),joined)
#else:
@ -99,18 +102,22 @@ class Library:
info.album = t.album
if t.artist != "":
info.artist = t.artist
#info.tnum = str(t.track)
a = f.audioProperties()
info.setTime(a.length)
except:
print info.filename
#print "here"
info.title = info.title.lower()
info.album = info.album.lower()
info.artist = info.artist.lower()
artist = info.artist
album = info.album
#info.title = info.title.lower()
#info.album = info.album.lower()
#info.artist = info.artist.lower()
artist = info.artist.lower()
info.artistl = artist
album = info.album.lower()
info.albuml = album
title = info.title.lower()
info.titlel = title
if (artist in self.artAlb.keys()) == False:
self.artAlb[artist] = [album]
@ -121,6 +128,24 @@ class Library:
self.albTrk[(artist,album)] = [index]
else:
self.albTrk[(artist,album)] += [index]
for letter in title:
info.single[letter] = True
for letter in artist:
info.single[letter] = True
for letter in album:
info.single[letter] = True
for i in range(len(title)-1):
str = title[i]+title[i+1]
info.double[str] = True
for i in range(len(artist)-1):
str = artist[i]+artist[i+1]
info.double[str] = True
for i in range(len(album)-1):
str = album[i]+album[i+1]
info.double[str] = True
self.libview.insertSong(info)
# Return true if file is in the library

View File

@ -11,6 +11,7 @@ class SavedData:
self.path = path
self.song = None
self.quit = None
self.scrobbler = None
if os.path.exists(self.path) == False:
os.mkdir(self.path)
self.load(path,options)
@ -19,10 +20,11 @@ class SavedData:
# Dump user data to a file
def dump(self):
self.library.libview = None
self.save(self.library,"library")
self.save([self.curList,self.curQ,self.curSong,self.playingQ],"playlist")
self.save([self.size,self.divider,self.colSizes,self.sortedCol,self.random],"preferences")
self.save(self.lfm,"last.fm")
self.save([self.lfm,self.lfmuser,self.lfmpass],"last.fm")
def save(self,obj,file):
@ -65,9 +67,15 @@ class SavedData:
self.sortedCol = prefs[3]
self.random = prefs[4]
self.lfm = self.load2("last.fm")
if self.lfm == None:
lfm = self.load2("last.fm")
if lfm == None:
self.lfm = ""
self.lfmuser = ""
self.lfmpass = ""
else:
self.lfm = lfm[0]
self.lfmuser = lfm[1]
self.lfmpass = lfm[2]
def load2(self,file):

View File

@ -1,3 +1,5 @@
import datetime
import time
import gst
@ -12,6 +14,7 @@ class Song():
self.bin.set_property("uri","file://"+self.info.filename)
self.bin.set_state(gst.STATE_PAUSED)
self.player.add(self.bin)
self.timestamp = str(int(time.mktime(datetime.datetime.now().timetuple())))
# initialize bus
bus = self.player.get_bus()
@ -52,9 +55,9 @@ class Song():
def stop(self):
self.player.set_state(gst.STATE_PAUSED)
self.playing = False
self.current = 0
self.position = 0
#self.player.seek_simple(self.time_format,gst.SEEK_FLAG_FLUSH,self.current)
self.seek(current)
self.seek(self.position)
def seek(self,time):
@ -68,7 +71,8 @@ class Song():
# Print out current running time
def curTime(self):
if self.playing == False:
return (False,False)
#if self.playing == False:
# return (True,self.position)
self.position = self.player.query_position(self.time_format,None)[0]/1000000000
return (True, self.position)
#return (True, self.position)
return self.position

View File

@ -4,13 +4,19 @@ class SongInfo:
def __init__(self):
self.id = 0
self.filename = ""
self.tnum = ""
self.count = 0
# Length is a string, duration is an int
self.length = ""
self.duration = 0
self.title = ""
self.titlel = ""
self.album = ""
self.albuml = ""
self.artist = ""
self.artistl = ""
self.single = dict()
self.double = dict()
def setTime(self,time):

View File

@ -74,6 +74,7 @@ class Window(gtk.Window):
# Used to select a directory
def selectDir(self,widgit,data,func):
self.deleteLib(None,None)
dirsel = gtk.FileChooserDialog(None,action=gtk.FILE_CHOOSER_ACTION_OPEN,buttons =(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
dirsel.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
response = None
@ -109,6 +110,7 @@ class Window(gtk.Window):
scrobLabel = gtk.Label("Last.fm")
scrobLabel.set_angle(90)
leftPane.append_page(scrobbler,scrobLabel)
self.data.scrobbler = scrobbler
self.divider.add1(leftPane)