From d057b2f455a985d6293c956e4481d27371f97eab Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Tue, 18 Dec 2012 19:35:04 -0500 Subject: [PATCH] Commit basic scanning support for songs --- archiver/archive.py | 46 +++++++++++++++++++++++++---------- archiver/song.py | 58 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/archiver/archive.py b/archiver/archive.py index d92bad2..f94a709 100644 --- a/archiver/archive.py +++ b/archiver/archive.py @@ -41,7 +41,7 @@ class Archive (models.Model): #And a reference to the songs in this archive 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" #This method is implemented since the other scan methods all need to use the same code #DRY FTW @@ -64,35 +64,28 @@ class Archive (models.Model): except ObjectDoesNotExist, e: #Song needs to be added to database - print "Adding song: " + filename full_url = os.path.join(dirname, filename) new_song = Song(url = full_url) - - f = open(full_url) - new_song.file_hash = hash(f.read()) new_song.populate_metadata() - new_song.save() - 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." from os.path import isfile #Validate existing database results for song in self.songs.all(): - if not isfile(song.song_url): + if not isfile(song.url): song.delete() #Scan the root folder, and find if we need to add any new songs - 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" + self._scan_filesystem() + 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 from Melodia.melodia_settings import HASH_FUNCTION as hash import os.path @@ -116,3 +109,30 @@ class Archive (models.Model): #Make sure to add any new songs as well 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() + diff --git a/archiver/song.py b/archiver/song.py index 721e207..8a6d564 100644 --- a/archiver/song.py +++ b/archiver/song.py @@ -23,7 +23,6 @@ class Song (models.Model): genre = models.CharField(max_length = 64) bpm = models.IntegerField() - #File metadata bit_rate = models.IntegerField() duration = models.IntegerField() @@ -31,17 +30,52 @@ class Song (models.Model): url = models.CharField(max_length = 64) 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" - #Will eventually use EchoNest to power this. For now, just use defaults. import datetime - self.title = "_" - self.artist = "_" - self.album = "_" - self.release_date = datetime.datetime.now() - self.genre = "_" - self.bpm = 0 - self.bit_rate = 0 - self.duration = 0 - self.echonest_song_id = "_" + if use_echonest: + #Code to grab metadata from echonest here + 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 '' + self.artist = track_metadata.artist_name or '' + self.album = track_metadata.album_name or '' + self.release_date = datetime.date(int(track_metadata.year or 1), 1, 1) + self.bpm = -1 + + self.bit_rate = track.bits_per_sample() or '' + self.duration = int(track.seconds_length()) or '' + 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 = "" + self.artist = "" + self.album = "" + 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()) +