Restructure relationship b/w Songs and Archives

Songs now have a ForeignKey to Archive - one song can not be part of
many Archives.
master
Bradlee Speice 2013-02-12 15:02:08 -05:00
parent 0d467e8457
commit 17960be548
2 changed files with 23 additions and 15 deletions

View File

@ -1,7 +1,5 @@
from django.db import models from django.db import models
from song import Song
""" """
This is the archive model for the archiving backend of Melodia. This is the archive model for the archiving backend of Melodia.
It's purpose is to control the high-level functionality of managing It's purpose is to control the high-level functionality of managing
@ -31,8 +29,8 @@ class Archive (models.Model):
#Note that we're not using FilePathField since this is actually a folder #Note that we're not using FilePathField since this is actually a folder
root_folder = models.CharField(max_length = 255) root_folder = models.CharField(max_length = 255)
#And a reference to the songs in this archive #We've removed the reference to "songs" - instead define it as a ForeignKey,
songs = models.ManyToManyField(Song) #and do lookups via song_set
#Backup settings #Backup settings
backup_location = models.CharField(max_length = 255, default = "/dev/null") backup_location = models.CharField(max_length = 255, default = "/dev/null")
@ -53,7 +51,7 @@ class Archive (models.Model):
#It's hackish, but far fewer transactions to delete everything first, and add it all back. #It's hackish, but far fewer transactions to delete everything first, and add it all back.
#If we get interrupted, just re-run it. #If we get interrupted, just re-run it.
self.songs.all().delete() song_set.all().delete()
#Add new songs #Add new songs
for dirname, dirnames, filenames in os.walk(self.root_folder): for dirname, dirnames, filenames in os.walk(self.root_folder):
@ -63,16 +61,16 @@ class Archive (models.Model):
full_url = os.path.abspath(rel_url) full_url = os.path.abspath(rel_url)
new_song = Song(url = full_url) new_song = Song(url = full_url)
new_song.save() new_song.save()
self.songs.add(new_song) song_set.add(new_song)
def _update_song_metadata(self, use_echonest = False, progress_callback = lambda x, y: None): def _update_song_metadata(self, use_echonest = False, progress_callback = lambda x, y: None):
"""Scan every song in this archive (database only) and make sure all songs are correct """Scan every song in this archive (database only) and make sure all songs are correct
The progress_callback function is called with the current song being operated on first, and the total songs second.""" The progress_callback function is called with the current song being operated on first, and the total songs second."""
#This method operates only on the songs that are in the database - if you need to make #This method operates only on the songs that are in the database - if you need to make
#sure that new songs are added, use the _scan_filesystem() method in addition #sure that new songs are added, use the _scan_filesystem() method in addition
total_songs = self.songs.count() total_songs = song_set.count()
for index, song in enumerate(self.songs.all()): for index, song in enumerate(song_set.all()):
song.populate_metadata(use_echonest = use_echonest) song.populate_metadata(use_echonest = use_echonest)
song.save() song.save()
progress_callback(index + 1, total_songs) progress_callback(index + 1, total_songs)
@ -133,9 +131,9 @@ class Archive (models.Model):
""" """
import os, shutil, errno import os, shutil, errno
total_songs = self.songs.count() total_songs = song_set.count()
for index, song in enumerate(self.songs.all()): for index, song in enumerate(song_set.all()):
_current_filename = os.path.basename(song.url) _current_filename = os.path.basename(song.url)
_current_filename_no_extension = os.path.splitext(_current_filename)[0] _current_filename_no_extension = os.path.splitext(_current_filename)[0]

View File

@ -1,7 +1,10 @@
from django.db import models from django.db import models
from Melodia import melodia_settings from Melodia import melodia_settings
from archive import Archive
import datetime import datetime
import os.path
""" """
The Song model The Song model
Each instance of a Song represents a single music file. Each instance of a Song represents a single music file.
@ -63,6 +66,9 @@ class Song (models.Model):
skip_count = models.IntegerField(default = _default_int) skip_count = models.IntegerField(default = _default_int)
rating = models.IntegerField(default = _default_int, choices = _default_rating_choices) rating = models.IntegerField(default = _default_int, choices = _default_rating_choices)
#Link back to the archive this comes from
parent_archive = models.ForeignKey(Archive)
#Set a static reference to the rating options #Set a static reference to the rating options
RATING_DEFAULT = _default_rating RATING_DEFAULT = _default_rating
RATING_BAD = _default_rating_bad RATING_BAD = _default_rating_bad
@ -71,6 +77,10 @@ class Song (models.Model):
RATING_GOOD = _default_rating_good RATING_GOOD = _default_rating_good
RATING_EXCELLENT = _default_rating_excellent RATING_EXCELLENT = _default_rating_excellent
def _get_full_url(self):
"Combine this song's URL with the URL of its parent"
return os.path.join(parent_archive.root_folder, self.url)
def _file_not_changed(self): def _file_not_changed(self):
"Make sure the hash for this file is valid - return True if it has not changed." "Make sure the hash for this file is valid - return True if it has not changed."
#Overload the hash function with whatever Melodia as a whole is using #Overload the hash function with whatever Melodia as a whole is using
@ -79,7 +89,7 @@ class Song (models.Model):
#Check if there's a hash entry - if there is, the song may not have changed, #Check if there's a hash entry - if there is, the song may not have changed,
#and we can go ahead and return #and we can go ahead and return
if self.file_hash != None: if self.file_hash != None:
song_file = open(self.url, 'rb') song_file = open(self._get_full_url, 'rb')
current_file_hash = hash(song_file.read()) current_file_hash = hash(song_file.read())
if current_file_hash == self.file_hash: if current_file_hash == self.file_hash:
@ -94,10 +104,10 @@ class Song (models.Model):
#Overload the hash function with whatever Melodia as a whole is using #Overload the 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
file_handle = open(self.url, 'rb') file_handle = open(self._get_full_url, 'rb')
self.file_hash = hash(file_handle.read()) self.file_hash = hash(file_handle.read())
self.file_size = os.stat(self.url).st_size self.file_size = os.stat(self._get_full_url).st_size
def _grab_metadata_echonest(self): def _grab_metadata_echonest(self):
"Populate this song's metadata using EchoNest" "Populate this song's metadata using EchoNest"
@ -110,8 +120,8 @@ class Song (models.Model):
try: try:
#Use mutagen to scan local metadata - don't update anything else (i.e. play_count) #Use mutagen to scan local metadata - don't update anything else (i.e. play_count)
track = mutagen.File(self.url) track = mutagen.File(self._get_full_url)
track_easy = mutagen.File(self.url, easy=True) track_easy = mutagen.File(self._get_full_url, easy=True)
self.title = track_easy['title'][0] or _default_string self.title = track_easy['title'][0] or _default_string
self.artist = track_easy['artist'][0] or _default_string self.artist = track_easy['artist'][0] or _default_string