Commit basic scanning support for songs

master
Bradlee Speice 2012-12-18 19:35:04 -05:00
parent d31961e029
commit d057b2f455
2 changed files with 79 additions and 25 deletions

View File

@ -41,7 +41,7 @@ class Archive (models.Model):
#And a reference to the songs in this archive #And a reference to the songs in this archive
songs = models.ManyToManyField(Song) songs = models.ManyToManyField(Song)
def _scan_filesystem(self): def _scan_filesystem(self, progress_callback = lambda x: None):
"Scan the archive's root filesystem and add any new songs" "Scan the archive's root filesystem and add any new songs"
#This method is implemented since the other scan methods all need to use the same code #This method is implemented since the other scan methods all need to use the same code
#DRY FTW #DRY FTW
@ -64,35 +64,28 @@ class Archive (models.Model):
except ObjectDoesNotExist, e: except ObjectDoesNotExist, e:
#Song needs to be added to database #Song needs to be added to database
print "Adding song: " + filename
full_url = os.path.join(dirname, filename) full_url = os.path.join(dirname, filename)
new_song = Song(url = full_url) new_song = Song(url = full_url)
f = open(full_url)
new_song.file_hash = hash(f.read())
new_song.populate_metadata() new_song.populate_metadata()
new_song.save() new_song.save()
self.songs.add(new_song) self.songs.add(new_song)
def scan(self): def quick_scan(self):
"Scan this archive's root folder and make sure that all songs are in the database." "Scan this archive's root folder and make sure that all songs are in the database."
from os.path import isfile from os.path import isfile
#Validate existing database results #Validate existing database results
for song in self.songs.all(): for song in self.songs.all():
if not isfile(song.song_url): if not isfile(song.url):
song.delete() song.delete()
#Scan the root folder, and find if we need to add any new songs #Scan the root folder, and find if we need to add any new songs
self._scan_filesystem self._scan_filesystem()
def deep_scan(self):
"Scan this archive's root folder and make sure that all songs are in the database, and update metadata as necessary"
def scan(self):
"Scan this archive's root folder and make sure any local metadata are correct."
#Overload the regular hash function with whatever Melodia as a whole is using #Overload the regular hash function with whatever Melodia as a whole is using
from Melodia.melodia_settings import HASH_FUNCTION as hash from Melodia.melodia_settings import HASH_FUNCTION as hash
import os.path import os.path
@ -116,3 +109,30 @@ class Archive (models.Model):
#Make sure to add any new songs as well #Make sure to add any new songs as well
self._scan_filesystem() self._scan_filesystem()
def deep_scan(self):
"Scan this archive's root folder and make sure that all songs are in the database, and use EchoNest to update metadata as necessary"
#Overload the regular hash function with whatever Melodia as a whole is using
from Melodia.melodia_settings import HASH_FUNCTION as hash
import os.path
for song in self.songs.all():
if not os.path.isfile(song.song_url):
song.delete()
continue
#The song exists, check that the hash is the same
db_hash = song.file_hash
f = open(song_url)
file_hash = hash(f.read())
if file_hash != db_hash:
#Something about the song has changed, rescan the metadata
song.populate_metadata(use_echonest = True)
#Make sure to add any new songs as well
self._scan_filesystem()

View File

@ -23,7 +23,6 @@ class Song (models.Model):
genre = models.CharField(max_length = 64) genre = models.CharField(max_length = 64)
bpm = models.IntegerField() bpm = models.IntegerField()
#File metadata #File metadata
bit_rate = models.IntegerField() bit_rate = models.IntegerField()
duration = models.IntegerField() duration = models.IntegerField()
@ -31,17 +30,52 @@ class Song (models.Model):
url = models.CharField(max_length = 64) url = models.CharField(max_length = 64)
file_hash = melodia_settings.HASH_RESULT_DB_TYPE file_hash = melodia_settings.HASH_RESULT_DB_TYPE
def populate_metadata(self): def populate_metadata(self, use_echonest = False, use_musicbrainz = False):
"Populate the metadata of this song" "Populate the metadata of this song"
#Will eventually use EchoNest to power this. For now, just use defaults.
import datetime import datetime
self.title = "_"
self.artist = "_"
self.album = "_"
self.release_date = datetime.datetime.now()
self.genre = "_"
self.bpm = 0
self.bit_rate = 0 if use_echonest:
self.duration = 0 #Code to grab metadata from echonest here
self.echonest_song_id = "_" pass
else:
#Grab metadata for the database using what is in the track.
from Melodia.resources import add_resource_dir
add_resource_dir()
import audiotools
try:
track = audiotools.open(self.url)
track_metadata = track.get_metadata()
self.title = track_metadata.track_name or '<UNAVAILABLE>'
self.artist = track_metadata.artist_name or '<UNAVAILABLE>'
self.album = track_metadata.album_name or '<UNAVAILABLE>'
self.release_date = datetime.date(int(track_metadata.year or 1), 1, 1)
self.bpm = -1
self.bit_rate = track.bits_per_sample() or '<UNAVAILABLE>'
self.duration = int(track.seconds_length()) or '<UNAVAILABLE>'
self.echonest_song_id = ''
except audiotools.UnsupportedFile, e:
#Couldn't grab the local data
#doesn't support the file, or because reading from it caused an error
self.title = "<UNAVAILABLE>"
self.artist = "<UNAVAILABLE>"
self.album = "<UNAVAILABLE>"
self.release_date = datetime.datetime.now()
self.bpm = -1
self.bit_rate = -1
self.duration = -1
self.echonest_song_id = ''
#Hash check is run regardless of what metadata method is used
if self.file_hash == None:
#Only get the hash if we really must, it's an expensive operation...
from Melodia.melodia_settings import HASH_FUNCTION as hash
f = open(self.url, 'rb')
self.file_hash = hash(f.read())