mirror of
https://github.com/bspeice/Melodia
synced 2025-07-16 05:15:26 -04:00
Create a models package, add template file suffix
Also, add AngularJS
This commit is contained in:
162
archiver/models/song.py
Normal file
162
archiver/models/song.py
Normal file
@ -0,0 +1,162 @@
|
||||
from django.db import models
|
||||
from Melodia import melodia_settings
|
||||
|
||||
from archive import Archive
|
||||
|
||||
import datetime
|
||||
import os.path
|
||||
"""
|
||||
The Song model
|
||||
Each instance of a Song represents a single music file.
|
||||
This database model is used for storing the metadata information about a song,
|
||||
and helps in doing sorting etc.
|
||||
"""
|
||||
|
||||
_default_string = "_UNAVAILABLE_"
|
||||
_default_date = datetime.datetime.now
|
||||
_default_int = -1
|
||||
|
||||
_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):
|
||||
class Meta:
|
||||
app_label = 'archiver'
|
||||
|
||||
"""
|
||||
This class defines the fields and functions related to controlling
|
||||
individual music files.
|
||||
Note that the Playlist model depends on this model's PK being an int.
|
||||
"""
|
||||
|
||||
#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_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)
|
||||
|
||||
#Link back to the archive this comes from
|
||||
parent_archive = models.ForeignKey(Archive)
|
||||
|
||||
#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 _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):
|
||||
"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
|
||||
from Melodia.melodia_settings import HASH_FUNCTION as hash
|
||||
|
||||
#Check if there's a hash entry - if there is, the song may not have changed,
|
||||
#and we can go ahead and return
|
||||
if self.file_hash != None:
|
||||
song_file = open(self._get_full_url, 'rb')
|
||||
current_file_hash = hash(song_file.read())
|
||||
|
||||
if current_file_hash == self.file_hash:
|
||||
#The song data hasn't changed at all, we don't need to do anything
|
||||
return True
|
||||
|
||||
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._get_full_url, 'rb')
|
||||
|
||||
self.file_hash = hash(file_handle.read())
|
||||
self.file_size = os.stat(self._get_full_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"
|
||||
#Use mutagen to get the song metadata
|
||||
import mutagen
|
||||
|
||||
try:
|
||||
#Use mutagen to scan local metadata - don't update anything else (i.e. play_count)
|
||||
track = mutagen.File(self._get_full_url)
|
||||
track_easy = mutagen.File(self._get_full_url, easy=True)
|
||||
|
||||
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.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
|
||||
|
||||
self.bit_rate = track.info.bitrate or _default_int
|
||||
self.duration = track.info.length or _default_int
|
||||
|
||||
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."
|
||||
if self._file_not_changed():
|
||||
return
|
||||
|
||||
#If we've gotten to here, we do actually need to fully update the metadata
|
||||
if use_echonest:
|
||||
self._grab_echonest()
|
||||
|
||||
else:
|
||||
self._grab_metadata_local()
|
||||
|
||||
def convert(self, output_location, output_format, progress_func = lambda x, y: None):
|
||||
"Convert a song to a new format."
|
||||
pass #Need to get pydub code in place
|
Reference in New Issue
Block a user