mirror of
https://github.com/bspeice/Melodia
synced 2024-12-25 08:08:13 -05:00
Heavy update to song database storage
Switch to mutagen for tagging, will use pydub for conversion. Change how song is represented in DB to be much more rich.
This commit is contained in:
parent
4f9b717674
commit
fdfcecc4a2
148
archiver/song.py
148
archiver/song.py
@ -9,20 +9,24 @@ This database model is used for storing the metadata information about a song,
|
||||
and helps in doing sorting etc.
|
||||
"""
|
||||
|
||||
_default_title = "_UNAVAILABLE_"
|
||||
_default_artist = "_UNAVAILABLE_"
|
||||
_default_album = "_UNAVAILABLE_"
|
||||
_default_release_date = datetime.datetime.now #Function will be called per new song, rather than only being called right now.
|
||||
_default_genre = "_UNAVAILABLE_"
|
||||
_default_bpm = -1
|
||||
_default_disc_number = -1
|
||||
_default_disc_total = -1
|
||||
_default_track_number = -1
|
||||
_default_track_total = -1
|
||||
_default_string = "_UNAVAILABLE_"
|
||||
_default_date = datetime.datetime.now
|
||||
_default_int = -1
|
||||
|
||||
_default_bit_rate = -1
|
||||
_default_duration = -1
|
||||
_default_echonest_song_id = ""
|
||||
_default_rating = 0
|
||||
_default_rating_bad = 1
|
||||
_default_rating_ok = 2
|
||||
_default_rating_decent = 3
|
||||
_default_rating_good = 4
|
||||
_default_rating_excellent = 5
|
||||
_default_rating_choices = (
|
||||
(_default_rating, 'Default'),
|
||||
(_default_rating_bad, 'Bad'),
|
||||
(_default_rating_ok, 'OK'),
|
||||
(_default_rating_decent, 'Decent'),
|
||||
(_default_rating_good, 'Good'),
|
||||
(_default_rating_excellent, 'Excellent'),
|
||||
)
|
||||
|
||||
class Song (models.Model):
|
||||
|
||||
@ -32,24 +36,40 @@ class Song (models.Model):
|
||||
Note that the Playlist model depends on this model's PK being an int.
|
||||
"""
|
||||
|
||||
#Standard user-populated metadata
|
||||
title = models.CharField(max_length = 64, default = _default_title)
|
||||
artist = models.CharField(max_length = 64, default = _default_artist)
|
||||
album = models.CharField(max_length = 64, default = _default_album)
|
||||
release_date = models.DateField(default = _default_release_date)
|
||||
genre = models.CharField(max_length = 64, default = _default_genre)
|
||||
bpm = models.IntegerField(default = _default_bpm)
|
||||
disc_number = models.IntegerField(default = _default_disc_number)
|
||||
disc_total = models.IntegerField(default = _default_disc_total)
|
||||
track_number = models.IntegerField(default = _default_track_number)
|
||||
track_total = models.IntegerField(default = _default_track_total)
|
||||
#Standard song metadata
|
||||
title = models.CharField(max_length = 64, default = _default_string)
|
||||
artist = models.CharField(max_length = 64, default = _default_string)
|
||||
album = models.CharField(max_length = 64, default = _default_string)
|
||||
year = models.IntegerField(default = _default_string)
|
||||
genre = models.CharField(max_length = 64, default = _default_string)
|
||||
bpm = models.IntegerField(default = _default_int)
|
||||
disc_number = models.IntegerField(default = _default_int)
|
||||
disc_total = models.IntegerField(default = _default_int)
|
||||
track_number = models.IntegerField(default = _default_int)
|
||||
track_total = models.IntegerField(default = _default_int)
|
||||
comment = models.CharField(default = _default_string, max_length=512)
|
||||
|
||||
#File metadata
|
||||
bit_rate = models.IntegerField(default = _default_bit_rate)
|
||||
duration = models.IntegerField(default = _default_bit_rate)
|
||||
echonest_song_id = models.CharField(max_length = 64, default = _default_echonest_song_id)
|
||||
bit_rate = models.IntegerField(default = _default_int)
|
||||
duration = models.FloatField(default = _default_int)
|
||||
add_date = models.DateField(default = _default_date)
|
||||
echonest_song_id = models.CharField(max_length = 64, default = _default_string)
|
||||
url = models.CharField(max_length = 255)
|
||||
file_hash = melodia_settings.HASH_RESULT_DB_TYPE
|
||||
file_size = models.IntegerField(default = _default_int)
|
||||
|
||||
#Melodia metadata
|
||||
play_count = models.IntegerField(default = _default_int)
|
||||
skip_count = models.IntegerField(default = _default_int)
|
||||
rating = models.IntegerField(default = _default_int, choices = _default_rating_choices)
|
||||
|
||||
#Set a static reference to the rating options
|
||||
RATING_DEFAULT = _default_rating
|
||||
RATING_BAD = _default_rating_bad
|
||||
RATING_OK = _default_rating_ok
|
||||
RATING_DECENT = _default_rating_decent
|
||||
RATING_GOOD = _default_rating_good
|
||||
RATING_EXCELLENT = _default_rating_excellent
|
||||
|
||||
def _file_not_changed(self):
|
||||
"Make sure the hash for this file is valid - return True if it has not changed."
|
||||
@ -68,47 +88,50 @@ class Song (models.Model):
|
||||
|
||||
return False
|
||||
|
||||
def _grab_file_info(self):
|
||||
"Populate file-based metadata about this song."
|
||||
import os
|
||||
#Overload the hash function with whatever Melodia as a whole is using
|
||||
from Melodia.melodia_settings import HASH_FUNCTION as hash
|
||||
|
||||
file_handle = open(self.url, 'rb')
|
||||
|
||||
self.file_hash = hash(file_handle.read())
|
||||
self.file_size = os.stat(self.url).st_size
|
||||
|
||||
def _grab_metadata_echonest(self):
|
||||
"Populate this song's metadata using EchoNest"
|
||||
pass
|
||||
|
||||
def _grab_metadata_local(self):
|
||||
"Populate this song's metadata using what is locally available"
|
||||
#It's weird, but we need to wrap importing audiotools
|
||||
from Melodia.resources import add_resource_dir
|
||||
add_resource_dir()
|
||||
import audiotools
|
||||
#Use mutagen to get the song metadata
|
||||
import mutagen
|
||||
|
||||
try:
|
||||
track = audiotools.open(self.url)
|
||||
track_metadata = track.get_metadata()
|
||||
#Use mutagen to scan local metadata - don't update anything else (i.e. play_count)
|
||||
track = mutagen.File(self.url)
|
||||
track_easy = mutagen.File(self.url, easy=True)
|
||||
|
||||
self.title = track_metadata.track_name or _default_title
|
||||
self.artist = track_metadata.artist_name or _default_artist
|
||||
self.album = track_metadata.album_name or _default_album
|
||||
self.release_date = datetime.date(int(track_metadata.year or 1), 1, 1)
|
||||
self.track_number = track_metadata.track_number or _default_track_number
|
||||
self.track_total = track_metadata.track_total or _default_track_total
|
||||
self.disc_number = track_metadata.album_number or _default_disc_number
|
||||
self.disc_total = track_metadata.album_total or _default_disc_total
|
||||
self.bpm = _default_bpm
|
||||
self.title = track_easy['title'][0] or _default_string
|
||||
self.artist = track_easy['artist'][0] or _default_string
|
||||
self.album_artist = track_easy['albumartist'][0] or _default_string
|
||||
self.album = track_easy['album'][0] or _default_string
|
||||
self.year = int(track_easy['date'][0][0:4]) or _default_int
|
||||
self.genre = track_easy["genre"][0] or _default_string
|
||||
|
||||
self.bit_rate = track.bits_per_sample() or _default_bit_rate
|
||||
self.duration = int(track.seconds_length()) or _default_duration
|
||||
self.echonest_song_id = _default_echonest_song_id
|
||||
self.disc_number = int(track_easy['discnumber'][0].split('/')[0]) or _default_int
|
||||
self.disc_total = int(track_easy['discnumber'][0].split('/')[-1]) or _default_int
|
||||
self.track_number = int(track_easy['track_number'][0].split('/')[0]) or _default_int
|
||||
self.track_total = int(track_easy['track_number'][0].split('/')[-1]) or _default_int
|
||||
self.comment = track_easy['comment'][0] or _default_string
|
||||
|
||||
except audiotools.UnsupportedFile, e:
|
||||
#Couldn't grab the local data - fill in the remaining data for this record, preserving
|
||||
#anything that already exists.
|
||||
self.title = self.title or _default_title
|
||||
self.artist = self.artist or _default_artist
|
||||
self.album = self.album or _default_album
|
||||
self.release_date = self.release_date or _default_release_date()
|
||||
self.bit_rate = track.info.bitrate or _default_int
|
||||
self.duration = track.info.length or _default_int
|
||||
|
||||
self.bpm = self.bpm or _default_bpm
|
||||
self.bit_rate = self.bit_rate or _default_bitrate
|
||||
self.duration = self.bit_rate or _default_duration
|
||||
self.echonest_song_id = self.echonest_song_id or _default_echonest_song_id
|
||||
except:
|
||||
#Couldn't grab the local data
|
||||
return False
|
||||
|
||||
def populate_metadata(self, use_echonest = False):
|
||||
"Populate the metadata of this song (only if file hash has changed), and save the result."
|
||||
@ -124,15 +147,4 @@ class Song (models.Model):
|
||||
|
||||
def convert(self, output_location, output_format, progress_func = lambda x, y: None):
|
||||
"Convert a song to a new format."
|
||||
#Note that output_format over-rides the format guessed by output_location
|
||||
|
||||
from Melodia.resources import add_resource_dir
|
||||
add_resource_dir()
|
||||
|
||||
import audiotools
|
||||
|
||||
convert_from = audiotools.open(self.url)
|
||||
convert_from.convert(output_location,
|
||||
output_format,
|
||||
progress_func)
|
||||
|
||||
pass #Need to get pydub code in place
|
||||
|
Loading…
Reference in New Issue
Block a user