Code

Ticket #305: music.py

File music.py, 9.8 KB (added by scanner@…, 9 years ago)

"music" app models/music.py file

Line 
1#
2# $Id$
3#
4# Description:
5#   This defines the model's for the "music" app in the mediaserv
6#   project. These models represent things like 'tracks' 'albums' etc.
7#
8
9import datetime
10
11# We import meta from django.core because that is where all the fields and like
12# stuff is defined.
13#
14from django.core import meta
15
16#############################################################################
17#
18# Here are the django "models" of objects that the mediaserv "music" app cares
19# about
20#
21
22#############################################################################
23#
24class MusicRoot(meta.Model):
25    """Simply because I wanted to store as much configuration information
26    information in our database as seemed useful I have the MusicRoot class. It
27    describes a path on the local file system were music files may be found.
28
29    You can have more than one MusicRoot, obviously.
30    """
31
32    fields = (
33        meta.CharField('directory', maxlength = 1024),
34        meta.DateTimeField('last_scan_started', blank = True, null = True),
35        meta.DateTimeField('last_scan_finished', blank = True, null = True),
36        )
37
38    admin = meta.Admin()
39   
40    #########################################################################
41    #
42    def __repr__(self):
43        return self.directory
44
45#############################################################################
46#
47class Artist(meta.Model):
48    """An artist. They will have a one to many relationship with
49    ArtistNames. They may have a simplified_name
50    name, and a many to many relationship with ArtistNames.
51
52    Not sure what other information we will have under the Artist object but it
53    seems to make sense to say that an artist is more than just a name.
54    """
55
56    fields = (
57        meta.CharField('comment', null = True, blank = True, maxlength = 1024),
58        meta.DateTimeField('date_added'),
59        )
60
61    admin = meta.Admin()
62   
63    #########################################################################
64    #
65    def __repr__(self):
66        """Return the highest preference artist name, if this artist has any
67        names. If they do not just return the id.
68        """
69        if self.get_artistname_count() > 0:
70            return repr(self.get_artistname_list()[0])
71        else:
72            return str(self.id)
73
74    #########################################################################
75    #
76    def get_simple_name(self):
77        """Look through all the names that this artist has and return the first
78        one that is marked as a simplified name.
79
80        If we find no simplified names then return None.
81        """
82        if self.get_artistname_count() == 0:
83            return None
84        names = self.get_artistname_list()
85        for name in names:
86            if name.simple_char_set:
87                return name
88        return None
89
90#############################################################################
91#
92class ArtistName(meta.Model):
93    """An artist's name. An artist may have more than one name. Each name has a
94    preference. The higher the preference the more this name is the one we
95    should use if we have to present a single name. This name may also be one
96    that is represented in a simple character set - suitable for devices like
97    the rio receiver that can not display kanji.
98    """
99    fields = (
100        meta.CharField('name', maxlength = 512, core = True, unique = True),
101        meta.IntegerField('preference', default = 0),
102        meta.BooleanField('simple_char_set'),
103        meta.ForeignKey(Artist, edit_inline = meta.TABULAR, num_in_admin = 3),
104        )
105
106    ordering = ['-preference']
107
108    #########################################################################
109    #
110    def __repr__(self):
111        return self.name
112
113#############################################################################
114#
115class Album(meta.Model):
116    """An album is a collection of tracks. A track can only appear in one
117    album.
118
119    Do we want to bother with artist/album relationships? I guess not, just get
120    the list of tracks, get the list of artists for the tracks and do a unique
121    set of those.
122
123    Note: We need to have a simple name/name relationship for albums but this
124    is not nearly as complex as it was for artists. Albums are typically only
125    known by one name. We just need a simplified version for devices like the
126    rio receiver.
127
128    Note: When you get the list of tracks associated with an album it should
129    give you the list sorted in the order indicated by the tracks' track_number
130    and disk_number.
131    """
132
133    fields = (
134        meta.CharField('name', maxlength = 512),
135        meta.CharField('simplified_name', maxlength = 512, blank = True,
136                       null = True),
137        meta.DateTimeField('date_added'),
138        )
139   
140    #########################################################################
141    #
142    def __repr__(self):
143        return self.name
144
145#############################################################################
146#
147class PlayList(meta.Model):
148    """Like an album this is a collection of tracks. Unlike an album, a track
149    can occur more than once in a play list.
150
151    Tracks in a play list need to have a defined order that is determined by
152    the playlist. Not sure how to do this yet.
153    """
154    fields = (
155        meta.CharField('name', maxlength = 512, unique = True),
156        meta.CharField('simplified_name', maxlength = 512, blank = True,
157                       null = True),
158        )
159
160    admin = meta.Admin()
161   
162    #########################################################################
163    #
164    def __repr__(self):
165        return self.name
166
167#############################################################################
168#
169class Genre(meta.Model):
170    """For better or for worse tracks have a fixed single genre field.
171    We will set the 'id' to be the same as the accepted standard set of genres.
172    """
173    fields = (
174        meta.CharField('name', maxlength = 256, unique = True),
175        meta.IntegerField('genre_id'),         # To map to the mp3 id field
176        )
177
178    #########################################################################
179    #
180    def __repr__(self):
181        return self.name
182
183#############################################################################
184#
185class Track(meta.Model):
186    """A track refers to a single playable file of sound/music media. The basic
187    fields are determined from what is available via the id3 tags.
188
189    However things like the 'album' and 'artist' are relations to other object
190    model instances.
191
192    We also keep track of some additional field so that we can echo it back to
193    iTunes potentially. Named things like 'last played time' and 'number of
194    times played.'
195
196    We also keep track of the encoding format of the track. Some players can
197    not play some encodings.
198
199    A track may also have a 'limited_name' field which is a representation of
200    the track's name in simple ASCII so that devices which can not display rich
201    character sets can display a simplified (aka romanized for Japanese track
202    names) instead of displaying gobbedly gook.
203    """
204
205    fields = (
206        meta.CharField('title', maxlength = 512),
207        meta.CharField('filename', maxlength = 1024, db_index = True),
208        meta.CharField('simplified_name', maxlength = 256, blank = True,
209                       null = True),
210        meta.IntegerField('year', null = True),
211        meta.IntegerField('play_time'),
212        meta.IntegerField('bit_rate'),
213        meta.BooleanField('vbr'),
214        meta.IntegerField('track_number', default = 0),
215        meta.IntegerField('disc_number', default = 0),
216        meta.IntegerField('play_count', default = 0), # tied to iTunes
217        meta.DateTimeField('last_scanned', blank = True, null = True),
218        meta.DateTimeField('last_played', null = True), # tied to iTunes
219        meta.IntegerField('bpm','beats per minute', null = True),  # iTunes
220        meta.CharField('grouping', maxlength = 512, null = True,
221                       blank = True),                 # tied to iTunes
222        meta.CharField('comments', maxlength = 1024, null = True,
223                       blank = True), # tied to iTunes
224        meta.ForeignKey(Artist, blank = True, null = True),
225        meta.ForeignKey(Album, blank = True, null = True),
226        meta.ManyToManyField(PlayList, blank = True, null = True),
227        meta.ForeignKey(Genre, blank = True, null = True),
228        meta.ForeignKey(MusicRoot),
229        )
230
231    ordering = ['track_number', 'disc_number']
232       
233    #########################################################################
234    #
235    def __repr__(self):
236        return self.title
237
238    #########################################################################
239    #
240    def play_time_string(self):
241        """Convert the playtime we have that is in seconds to a friendlier
242        human readable string. This was cribbed from
243        Eye3D.tag.getPlayTimeString()
244        """
245        total = self.play_time
246        h = total / 3600
247        m = (total % 3600) / 60
248        s = (total % 3600) % 60
249        if h:
250            timeStr = "%d:%.2d:%.2d" % (h, m, s)
251        else:
252            timeStr = "%d:%.2d" % (m, s)
253        return timeStr
254       
255#############################################################################
256#
257class Artwork(meta.Model):
258    """iTunes lets us associate multiple pieces of artwork with tracks.
259    I am thinking of letting albums also have artwork associated with them (who
260    would see this, though? Only the web interface so far.)
261
262    A track may have more than one piece of art associated with it.
263    """
264
265    fields = (
266        meta.ImageField('image'),
267        # Maybe we should store some of the file's attributes in our structure?
268        # Image size? encoding?
269        meta.ManyToManyField(Track),
270        )
271
272    admin = meta.Admin()
273   
274    #########################################################################
275    #
276##    def __repr__(self):
277##        return self.file_name
278