mirror of
https://github.com/bspeice/Melodia
synced 2024-12-26 00:28: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.
|
and helps in doing sorting etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_title = "_UNAVAILABLE_"
|
_default_string = "_UNAVAILABLE_"
|
||||||
_default_artist = "_UNAVAILABLE_"
|
_default_date = datetime.datetime.now
|
||||||
_default_album = "_UNAVAILABLE_"
|
_default_int = -1
|
||||||
_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_bit_rate = -1
|
_default_rating = 0
|
||||||
_default_duration = -1
|
_default_rating_bad = 1
|
||||||
_default_echonest_song_id = ""
|
_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):
|
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.
|
Note that the Playlist model depends on this model's PK being an int.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#Standard user-populated metadata
|
#Standard song metadata
|
||||||
title = models.CharField(max_length = 64, default = _default_title)
|
title = models.CharField(max_length = 64, default = _default_string)
|
||||||
artist = models.CharField(max_length = 64, default = _default_artist)
|
artist = models.CharField(max_length = 64, default = _default_string)
|
||||||
album = models.CharField(max_length = 64, default = _default_album)
|
album = models.CharField(max_length = 64, default = _default_string)
|
||||||
release_date = models.DateField(default = _default_release_date)
|
year = models.IntegerField(default = _default_string)
|
||||||
genre = models.CharField(max_length = 64, default = _default_genre)
|
genre = models.CharField(max_length = 64, default = _default_string)
|
||||||
bpm = models.IntegerField(default = _default_bpm)
|
bpm = models.IntegerField(default = _default_int)
|
||||||
disc_number = models.IntegerField(default = _default_disc_number)
|
disc_number = models.IntegerField(default = _default_int)
|
||||||
disc_total = models.IntegerField(default = _default_disc_total)
|
disc_total = models.IntegerField(default = _default_int)
|
||||||
track_number = models.IntegerField(default = _default_track_number)
|
track_number = models.IntegerField(default = _default_int)
|
||||||
track_total = models.IntegerField(default = _default_track_total)
|
track_total = models.IntegerField(default = _default_int)
|
||||||
|
comment = models.CharField(default = _default_string, max_length=512)
|
||||||
|
|
||||||
#File metadata
|
#File metadata
|
||||||
bit_rate = models.IntegerField(default = _default_bit_rate)
|
bit_rate = models.IntegerField(default = _default_int)
|
||||||
duration = models.IntegerField(default = _default_bit_rate)
|
duration = models.FloatField(default = _default_int)
|
||||||
echonest_song_id = models.CharField(max_length = 64, default = _default_echonest_song_id)
|
add_date = models.DateField(default = _default_date)
|
||||||
|
echonest_song_id = models.CharField(max_length = 64, default = _default_string)
|
||||||
url = models.CharField(max_length = 255)
|
url = models.CharField(max_length = 255)
|
||||||
file_hash = melodia_settings.HASH_RESULT_DB_TYPE
|
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):
|
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."
|
||||||
@ -68,47 +88,50 @@ class Song (models.Model):
|
|||||||
|
|
||||||
return False
|
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):
|
def _grab_metadata_echonest(self):
|
||||||
"Populate this song's metadata using EchoNest"
|
"Populate this song's metadata using EchoNest"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _grab_metadata_local(self):
|
def _grab_metadata_local(self):
|
||||||
"Populate this song's metadata using what is locally available"
|
"Populate this song's metadata using what is locally available"
|
||||||
#It's weird, but we need to wrap importing audiotools
|
#Use mutagen to get the song metadata
|
||||||
from Melodia.resources import add_resource_dir
|
import mutagen
|
||||||
add_resource_dir()
|
|
||||||
import audiotools
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
track = audiotools.open(self.url)
|
#Use mutagen to scan local metadata - don't update anything else (i.e. play_count)
|
||||||
track_metadata = track.get_metadata()
|
track = mutagen.File(self.url)
|
||||||
|
track_easy = mutagen.File(self.url, easy=True)
|
||||||
|
|
||||||
self.title = track_metadata.track_name or _default_title
|
self.title = track_easy['title'][0] or _default_string
|
||||||
self.artist = track_metadata.artist_name or _default_artist
|
self.artist = track_easy['artist'][0] or _default_string
|
||||||
self.album = track_metadata.album_name or _default_album
|
self.album_artist = track_easy['albumartist'][0] or _default_string
|
||||||
self.release_date = datetime.date(int(track_metadata.year or 1), 1, 1)
|
self.album = track_easy['album'][0] or _default_string
|
||||||
self.track_number = track_metadata.track_number or _default_track_number
|
self.year = int(track_easy['date'][0][0:4]) or _default_int
|
||||||
self.track_total = track_metadata.track_total or _default_track_total
|
self.genre = track_easy["genre"][0] or _default_string
|
||||||
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.bit_rate = track.bits_per_sample() or _default_bit_rate
|
self.disc_number = int(track_easy['discnumber'][0].split('/')[0]) or _default_int
|
||||||
self.duration = int(track.seconds_length()) or _default_duration
|
self.disc_total = int(track_easy['discnumber'][0].split('/')[-1]) or _default_int
|
||||||
self.echonest_song_id = _default_echonest_song_id
|
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:
|
self.bit_rate = track.info.bitrate or _default_int
|
||||||
#Couldn't grab the local data - fill in the remaining data for this record, preserving
|
self.duration = track.info.length or _default_int
|
||||||
#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.bpm = self.bpm or _default_bpm
|
except:
|
||||||
self.bit_rate = self.bit_rate or _default_bitrate
|
#Couldn't grab the local data
|
||||||
self.duration = self.bit_rate or _default_duration
|
return False
|
||||||
self.echonest_song_id = self.echonest_song_id or _default_echonest_song_id
|
|
||||||
|
|
||||||
def populate_metadata(self, use_echonest = False):
|
def populate_metadata(self, use_echonest = False):
|
||||||
"Populate the metadata of this song (only if file hash has changed), and save the result."
|
"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):
|
def convert(self, output_location, output_format, progress_func = lambda x, y: None):
|
||||||
"Convert a song to a new format."
|
"Convert a song to a new format."
|
||||||
#Note that output_format over-rides the format guessed by output_location
|
pass #Need to get pydub code in place
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user