mirror of
				https://github.com/bspeice/Melodia
				synced 2025-10-31 17:30:42 -04:00 
			
		
		
		
	Upload the initial documentation for the archiver application
This commit is contained in:
		
							
								
								
									
										56
									
								
								archiver/listfield.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								archiver/listfield.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | ''' | ||||||
|  | Testing documentation | ||||||
|  | ''' | ||||||
|  | from django.db import models | ||||||
|  | import re, itertools | ||||||
|  |  | ||||||
|  | class IntegerListField(models.TextField): | ||||||
|  | 	""" | ||||||
|  | 	Store a list of integers in a database string. | ||||||
|  | 	Format is:  | ||||||
|  | 	[<int_1>, <int_2>, <int_3>, ... , <int_n>] | ||||||
|  | 	""" | ||||||
|  |  | ||||||
|  | 	description = "Field type for storing lists of integers." | ||||||
|  |  | ||||||
|  | 	__metaclass__ = models.SubfieldBase | ||||||
|  |  | ||||||
|  | 	def __init__(self, *args, **kwargs): | ||||||
|  | 		super(IntegerListField, self).__init__(*args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	#Convert database to python | ||||||
|  | 	def to_python(self, value): | ||||||
|  | 		if isinstance(value, list): | ||||||
|  | 			return value | ||||||
|  |  | ||||||
|  | 		#Process a database string | ||||||
|  | 		 | ||||||
|  | 		#Validation first | ||||||
|  | 		if len(value) <= 0: | ||||||
|  | 			return [] | ||||||
|  |  | ||||||
|  | 		if value[0] != '[' or value[-1] != ']': | ||||||
|  | 			raise ValidationError("Invalid input to parse a list of integers!") | ||||||
|  |  | ||||||
|  | 		#Note that any non-digit string is a valid separator | ||||||
|  | 		_csv_regex = "[0-9]" | ||||||
|  | 		csv_regex  = re.compile(_csv_regex) | ||||||
|  |  | ||||||
|  | 		#Synonymous to: | ||||||
|  | 		#string_list = filter(None, csv_regex.findall(value)) | ||||||
|  | 		string_list  = itertools.ifilter(None, csv_regex.findall(value)) | ||||||
|  | 		value_list   = [int(i) for i in string_list] | ||||||
|  |  | ||||||
|  | 		return value_list | ||||||
|  |  | ||||||
|  | 	#Convert python to database | ||||||
|  | 	def get_prep_value(self, value): | ||||||
|  | 		if not isinstance(value, list): | ||||||
|  | 			raise ValidationError("Invalid list given to put in database!") | ||||||
|  |  | ||||||
|  | 		separator_string = ", " | ||||||
|  |  | ||||||
|  | 		list_elements = separator_string.join(map(str, value)) | ||||||
|  |  | ||||||
|  | 		return "[" + list_elements + "]" | ||||||
| @ -1,10 +1,3 @@ | |||||||
| ''' |  | ||||||
| .. currentmodule:: archiver.models |  | ||||||
|  |  | ||||||
| I'm trying to link to :class:`~archiver.models.archive.Archive`! |  | ||||||
|  |  | ||||||
| ''' |  | ||||||
|  |  | ||||||
| # Create your models here. | # Create your models here. | ||||||
| from archive import Archive | from archive import Archive | ||||||
| from song import Song | from song import Song | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| """ | """ | ||||||
| .. module:: archiver.models.archive |  | ||||||
|  |  | ||||||
| This is the Archive model for the backend of Melodia. It's functionality is to | This is the Archive model for the backend of Melodia. It's functionality is to | ||||||
| provide a grouping of songs based on where they are located in the filesystem. | provide a grouping of songs based on where they are located in the filesystem. | ||||||
| It controls the high-level functionality of managing multiple archives | It controls the high-level functionality of managing multiple archives | ||||||
|  | |||||||
| @ -113,5 +113,8 @@ class Feed(models.Model): | |||||||
| 		:param dry_run: Calculate what would have been downloaded or deleted, but do not actually do either. | 		:param dry_run: Calculate what would have been downloaded or deleted, but do not actually do either. | ||||||
| 		:param forbid_delete: Run, and only download new episodes. Ignores the :data:`max_episodes` field for this podcast. | 		:param forbid_delete: Run, and only download new episodes. Ignores the :data:`max_episodes` field for this podcast. | ||||||
|  |  | ||||||
|  | 		.. todo:: | ||||||
|  | 		   Actually write this method... | ||||||
|  |  | ||||||
|         """ |         """ | ||||||
|         pass |         pass | ||||||
|  | |||||||
| @ -1,24 +1,17 @@ | |||||||
| """ | """ | ||||||
| .. module:: archiver.models | The Playlist model is simply that - it's a playlist of songs. However, we do | ||||||
|  | have to guarantee the song order, in addition to re-ordering the playlist. | ||||||
| Playlist model | As such, a :class:`models.ManyToManyField` isn't sufficient. We use a custom | ||||||
| Each playlist is a high-level ordering of songs. There really isn't much to a playlist - just its name, and the songs inside it. | database field to store a list of integers - the :class:`IntegerListField`. | ||||||
| However, we need to have a way to guarantee song order, in addition to re-ordering. A ManyToMany field can't do this. | This way we can guarantee song order, re-order the playlist, have songs | ||||||
| As such, a custom IntegerListField is implemented - it takes a python list of ints, converts it to a text field in the DB, | appear multiple times, etc. | ||||||
| and then back to a python list. This way, we can guarantee order, and have a song appear multiple times. |  | ||||||
| The IntegerListField itself uses the ID of each song as the int in a list. For example, a list of: |  | ||||||
|  |  | ||||||
|    [1, 3, 5, 17] |  | ||||||
|  |  | ||||||
| Means that the playlist is made up of four songs. The order of the playlist is the song with index 1, 3, 5, and 17. |  | ||||||
| Additionally, the ManyToMany field is included to make sure we don't use the global Songs manager - it just seems hackish. |  | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.core.exceptions import ObjectDoesNotExist | from django.core.exceptions import ObjectDoesNotExist | ||||||
|  |  | ||||||
| from song import Song | from song import Song | ||||||
| from listfield import IntegerListField | from archiver.listfield import IntegerListField | ||||||
|  |  | ||||||
| import re | import re | ||||||
| from warnings import warn | from warnings import warn | ||||||
| @ -28,83 +21,74 @@ class Playlist(models.Model): | |||||||
| 		app_label = 'archiver' | 		app_label = 'archiver' | ||||||
|  |  | ||||||
| 	""" | 	""" | ||||||
| 	The Playlist class defines the playlist model and its operations. | 	.. data:: name | ||||||
| 	Currently supported are add, move, and remove operations, as well as exporting to |  | ||||||
| 	multiple formats. | 	   String with the human-readable name for this playlist. | ||||||
|  |  | ||||||
|  | 	.. data:: song_list | ||||||
|  |  | ||||||
|  | 	   List made up of Python integers. Each integer is assumed | ||||||
|  | 	   to be a primary key to the :data:`Song.id` field for a song. | ||||||
| 	""" | 	""" | ||||||
|  |  | ||||||
| 	name       = models.CharField(max_length = 255) | 	name       = models.CharField(max_length = 255) | ||||||
| 	song_list = IntegerListField() | 	song_list = IntegerListField() | ||||||
|  |  | ||||||
| 	#This is a bit of a backup field, since technically the song PK's are in |  | ||||||
| 	#the song_order field. However, it might be useful to just get a reference |  | ||||||
| 	#to the songs in this playlist. Additionally, it's kind of hackish to reference |  | ||||||
| 	#the global Song manager, rather than something that is specific for this playlist. |  | ||||||
| 	songs      = models.ManyToManyField(Song) |  | ||||||
|  |  | ||||||
| 	def _populate_songs(self): |  | ||||||
| 		""" |  | ||||||
| 		Make sure that the 'songs' relation is up-to-date. |  | ||||||
| 		""" |  | ||||||
| 		#This operation works by getting the ID's for all songs currently in the playlist, |  | ||||||
| 		#calculates what we need to add, what we need to remove, and then does it. |  | ||||||
| 		#As much work as is possible is done on the python side to avoid the DB at all costs. |  | ||||||
| 		current_song_ids     = [song.id for song in self.songs.all()] |  | ||||||
| 		current_song_ids_set = set(current_song_ids) |  | ||||||
|  |  | ||||||
| 		new_song_ids_set = set(self.song_list) |  | ||||||
|  |  | ||||||
| 		remove_set = current_song_ids_set.difference(new_song_ids_set) |  | ||||||
| 		add_set    = new_song_ids_set.difference(current_song_ids_set) |  | ||||||
|  |  | ||||||
| 		for song_id in remove_set: |  | ||||||
| 			song = self.songs.get(id = song_id) |  | ||||||
| 			song.remove() |  | ||||||
|  |  | ||||||
| 		for song_id in add_set: |  | ||||||
| 			song = Song.objects.get(id = song_id) #Using the global Songs manager is unavoidable for this one |  | ||||||
| 			self.songs.add(song) |  | ||||||
|  |  | ||||||
| 	def insert(self, position, new_song): | 	def insert(self, position, new_song): | ||||||
| 		""" | 		""" | ||||||
| 		Insert a new song into the playlist at a specific position. | 		Insert a new song into the playlist at a specific position. | ||||||
|  |  | ||||||
|  | 		:param position: **Index** for the position this new song should be inserted at. | ||||||
|  | 		:param new_song: Reference to a :class:`Song` instance that will be inserted. | ||||||
| 		""" | 		""" | ||||||
|  |  | ||||||
| 		if not isinstance(new_song, Song): | 		if not isinstance(new_song, Song): | ||||||
| 			#Not given a song reference, raise an error | 			#Not given a song reference, raise an error | ||||||
| 			raise ValidationError("Not given a song reference to insert.") | 			raise ValidationError("Not given a song reference to insert.") | ||||||
| 			 | 			 | ||||||
| 		self.songs.add(new_song) |  | ||||||
|  |  | ||||||
| 		self.song_list.insert(position, new_song.id) | 		self.song_list.insert(position, new_song.id) | ||||||
|  |  | ||||||
| 		self._populate_songs() |  | ||||||
|  |  | ||||||
| 	def append(self, new_song): | 	def append(self, new_song): | ||||||
| 		""" | 		""" | ||||||
| 		Add a new song to the end of the playlist. | 		Add a new song to the end of the playlist. | ||||||
|  |  | ||||||
|  | 		:param new_song: Reference to a :class:`Song` instance to be appended. | ||||||
| 		""" | 		""" | ||||||
|  |  | ||||||
| 		if not isinstance(new_song, Song): | 		if not isinstance(new_song, Song): | ||||||
| 			#Not given a song reference, raise an error | 			#Not given a song reference, raise an error | ||||||
| 			raise ValidationError("Not given a song reference to insert.") | 			raise ValidationError("Not given a song reference to insert.") | ||||||
|  |  | ||||||
| 		self.songs.add(new_song) |  | ||||||
|  |  | ||||||
| 		self.song_list.append(new_song.id) | 		self.song_list.append(new_song.id) | ||||||
|  |  | ||||||
| 		self._populate_songs() |  | ||||||
|  |  | ||||||
| 	def move(self, original_position, new_position): | 	def move(self, original_position, new_position): | ||||||
| 		""" | 		""" | ||||||
| 		Move a song from one position to another | 		Move a song from one position to another | ||||||
|  |  | ||||||
|  | 		:param original_position: The index of the song we want to move | ||||||
|  | 		:param new_position: The index of where the song should be. See note below. | ||||||
|  |  | ||||||
|  | 		.. note:: | ||||||
|  |  | ||||||
|  | 		   When moving songs, it's a bit weird since the index we're actually | ||||||
|  | 		   moving to may change. Consider the scenario -- | ||||||
|  |  | ||||||
|  | 		      * Function called with indexes 4 and 6 | ||||||
|  |  | ||||||
|  | 		      * Song removed from index 4  | ||||||
|  |  | ||||||
|  | 		        * The song that was in index 6 is now at index 5 | ||||||
|  |  | ||||||
|  | 		      * Song inserted at index 6 in new list - one further than originally intended. | ||||||
|  |  | ||||||
|  | 		   As such, the behavior is that the song at index ``original_position`` is placed | ||||||
|  | 		   above the song at ``new_position`` when this function is called. | ||||||
| 		""" | 		""" | ||||||
| 		if original_position == new_position: | 		if original_position == new_position: | ||||||
| 			return | 			return | ||||||
|  |  | ||||||
| 		song_id = self.song_list[original_position] | 		song_id = self.song_list[original_position] | ||||||
|  |  | ||||||
| 		#This is actually a bit more complicated than it first appears, since the index we're moving to may actually change |  | ||||||
| 		#when we remove an item. |  | ||||||
| 		if new_position < original_position: | 		if new_position < original_position: | ||||||
| 			del self.song_list[original_position] | 			del self.song_list[original_position] | ||||||
| 			self.song_list.insert(new_position, song_id) | 			self.song_list.insert(new_position, song_id) | ||||||
| @ -113,25 +97,29 @@ class Playlist(models.Model): | |||||||
| 			del self.song_list[original_position] | 			del self.song_list[original_position] | ||||||
| 			self.song_list.insert(new_position - 1, song_id) #Account for the list indices shifting down. | 			self.song_list.insert(new_position - 1, song_id) #Account for the list indices shifting down. | ||||||
|  |  | ||||||
| 		self._populate_songs() |  | ||||||
|  |  | ||||||
| 	def remove(self, position): | 	def remove(self, position): | ||||||
|  | 		""" | ||||||
|  | 		Remove a song from this playlist. | ||||||
|  |  | ||||||
|  | 		:param position: Index of the song to be removed | ||||||
|  | 		""" | ||||||
| 		if position > len(self.song_list): | 		if position > len(self.song_list): | ||||||
| 			return False | 			return False | ||||||
|  |  | ||||||
| 		del self.song_list[position] | 		del self.song_list[position] | ||||||
|  |  | ||||||
| 		self._populate_songs() |  | ||||||
|  |  | ||||||
| 	def export(self, playlist_type = "m3u"): | 	def export(self, playlist_type = "m3u"): | ||||||
| 		""" | 		""" | ||||||
| 		Export this internal playlist to a file format. | 		Export this internal playlist to a file format. | ||||||
| 		Supported formats: | 		Supported formats: | ||||||
| 			-pls |  | ||||||
| 			-m3u | 		   * pls | ||||||
| 		Return value is a string containing the playlist - | 		   * m3u | ||||||
| 		you can write it to file as you deem necessary. |  | ||||||
|  | 		:param playlist_type: String containing the file type to export to | ||||||
|  | 		:rtype: String containing the file content for this playlist. | ||||||
| 		""" | 		""" | ||||||
|  |  | ||||||
| 		if playlist_type == "pls": | 		if playlist_type == "pls": | ||||||
| 			#Playlist header | 			#Playlist header | ||||||
| 			playlist_string = "[playlist]" | 			playlist_string = "[playlist]" | ||||||
| @ -162,13 +150,26 @@ class Playlist(models.Model): | |||||||
|  |  | ||||||
| 			return playlist_string | 			return playlist_string | ||||||
|  |  | ||||||
| 	def _import(self, playlist_string = None): | 	def playlist_import(self, playlist_string = None): | ||||||
| 		""" | 		""" | ||||||
| 		Import and convert a playlist into native DB format. | 		Import and convert a playlist into native DB format. | ||||||
| 		This function will return true if the playlist format was recognized, false otherwise. |  | ||||||
| 		It will return true even if there are errors processing individual songs in the playlist. | 		:param playlist_string: A string with the file content we're trying to import. | ||||||
| 		As a side note - the _import() name is used since python doesn't let |  | ||||||
| 		you name a function import(). | 		:rtype: Returns true of the playlist format was recognized. See notes on processing below. | ||||||
|  |  | ||||||
|  | 		.. warning:: | ||||||
|  |  | ||||||
|  | 		   The semantics on returning are nitpicky. This function will return ``False`` if the | ||||||
|  | 		   playlist format was not recognized. If there are errors in processing, this | ||||||
|  | 		   function will still return ``True``. | ||||||
|  |  | ||||||
|  | 		   For example, if you try to import a song which does not exist in an :class:`Archive`, | ||||||
|  | 		   it will fail that song silently. | ||||||
|  |  | ||||||
|  | 		.. todo:: | ||||||
|  |  | ||||||
|  | 		   Actually write the import code. | ||||||
| 		""" | 		""" | ||||||
| 		#TODO: Code playlist importing | 		#TODO: Code playlist importing | ||||||
| 		self.song_list = [] | 		self.song_list = [] | ||||||
|  | |||||||
| @ -1,3 +1,9 @@ | |||||||
|  | """ | ||||||
|  | The :class:`Song` model is by far the most complicated and involved model. | ||||||
|  | Each instance is a single music file. This model is used to store metadata | ||||||
|  | about the song. | ||||||
|  | """ | ||||||
|  |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from Melodia import melodia_settings | from Melodia import melodia_settings | ||||||
|  |  | ||||||
| @ -5,12 +11,6 @@ from archive import Archive | |||||||
|  |  | ||||||
| import datetime | import datetime | ||||||
| import os.path | 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_string = "_UNAVAILABLE_" | ||||||
| _default_date   = datetime.datetime.now | _default_date   = datetime.datetime.now | ||||||
| @ -32,18 +32,119 @@ _default_rating_choices = ( | |||||||
| 		) | 		) | ||||||
|  |  | ||||||
| class Song (models.Model): | class Song (models.Model): | ||||||
| 	class Meta: |  | ||||||
| 		app_label = 'archiver' |  | ||||||
| 	 |  | ||||||
| 	""" | 	""" | ||||||
| 	This class defines the fields and functions related to controlling | 	.. data:: title | ||||||
| 	individual music files. | 	    | ||||||
| 	Note that the Playlist model depends on this model's PK being an int. | 	   Title tag of this song | ||||||
|  |  | ||||||
|  | 	.. data:: artist | ||||||
|  |  | ||||||
|  | 	   Artist tag of this song. | ||||||
|  |  | ||||||
|  | 	.. data:: album_artist | ||||||
|  |  | ||||||
|  | 	   Album artist tag of this song. Can be used to group albums where | ||||||
|  | 	   individual songs were made by different people. | ||||||
|  |  | ||||||
|  | 	.. data:: album | ||||||
|  |  | ||||||
|  | 	   Album tag of this song. | ||||||
|  |  | ||||||
|  | 	.. data:: year | ||||||
|  | 	 | ||||||
|  | 	   Integer representing the year this song was made. | ||||||
|  |  | ||||||
|  | 	.. data:: genre | ||||||
|  |  | ||||||
|  | 	   Genre tag of this song. This is a general :class:`models.CharField` | ||||||
|  | 	   field, and is not limited to a specific set of genres. | ||||||
|  |  | ||||||
|  | 	.. data:: bpm | ||||||
|  |  | ||||||
|  | 	   Beats per minute of this song (integer). | ||||||
|  |  | ||||||
|  | 	.. data:: disc_number | ||||||
|  |  | ||||||
|  | 	   Disc number this song came from | ||||||
|  |  | ||||||
|  | 	.. data:: disc_total | ||||||
|  |  | ||||||
|  | 	   Total number of discs in the album this song is from | ||||||
|  |  | ||||||
|  | 	.. data:: track_number | ||||||
|  |  | ||||||
|  | 	   Track number in the album this song is from | ||||||
|  |  | ||||||
|  | 	.. data:: track_total | ||||||
|  |  | ||||||
|  | 	   Total number of tracks in the album this song is from | ||||||
|  |  | ||||||
|  | 	.. data:: comment | ||||||
|  |  | ||||||
|  | 	   Comment tag of this song | ||||||
|  |  | ||||||
|  | 	.. data:: bit_rate | ||||||
|  |  | ||||||
|  | 	   Integer representing the bit rate of this song | ||||||
|  |  | ||||||
|  | 	.. data:: duration | ||||||
|  | 	 | ||||||
|  | 	   Duration (in seconds, floating-point value) of this song | ||||||
|  |  | ||||||
|  | 	.. data:: add_date | ||||||
|  |  | ||||||
|  | 	   Date (not time) this song was added to the DB. Should **not** be | ||||||
|  | 	   modified outside of this class' methods. | ||||||
|  |  | ||||||
|  | 	.. data:: url | ||||||
|  |  | ||||||
|  | 	   URL for where this file is located on disk. | ||||||
|  |  | ||||||
|  | 	.. data:: file_hash | ||||||
|  |  | ||||||
|  | 	   The hash string for this file - used to quickly check if the file has | ||||||
|  | 	   been modified. | ||||||
|  |  | ||||||
|  | 	.. data:: file_size | ||||||
|  |  | ||||||
|  | 	   Size of the file in bytes. | ||||||
|  |  | ||||||
|  | 	.. data:: play_count | ||||||
|  |  | ||||||
|  | 	   How many times this file has been played through (defined as greater | ||||||
|  | 	   than 50% of the song heard before skipping) | ||||||
|  |  | ||||||
|  | 	.. data:: skip_count | ||||||
|  |  | ||||||
|  | 	   How many times this file has been skipped (defined as less than 50% of | ||||||
|  | 	   the song heard before skipping) | ||||||
|  |  | ||||||
|  | 	.. data:: rating | ||||||
|  |  | ||||||
|  | 	   Rating for this song. Ratings are as follows in order of increasing favoredness  | ||||||
|  | 	   on a 1--5 scale -- | ||||||
|  |  | ||||||
|  | 	   =========    ========    ================ | ||||||
|  | 	   Rating:      Value:      Class field: | ||||||
|  | 	   =========    ========    ================ | ||||||
|  | 	   Default      0           RATING_DEFAULT | ||||||
|  | 	   Bad          1           RATING_BAD | ||||||
|  | 	   OK           2           RATING_OK | ||||||
|  | 	   Decent       3           RATING_DECENT | ||||||
|  | 	   Good         4           RATING_GOOD | ||||||
|  | 	   Excellent    5           RATING_EXCELLENT | ||||||
|  | 	   =========    ========    ================ | ||||||
|  |  | ||||||
|  | 	.. todo::  | ||||||
|  |  | ||||||
|  | 	   Change defaults to allow for ``None`` instead | ||||||
|  | 	   Change private functions to public as need be | ||||||
| 	""" | 	""" | ||||||
|  |  | ||||||
| 	#Standard song metadata | 	#Standard song metadata | ||||||
| 	title        = models.CharField(max_length = 64, default = _default_string) | 	title        = models.CharField(max_length = 64, default = _default_string) | ||||||
| 	artist       = models.CharField(max_length = 64, default = _default_string) | 	artist       = models.CharField(max_length = 64, default = _default_string) | ||||||
|  | 	album_artist = models.CharField(max_length = 64, default = _default_string) | ||||||
| 	album        = models.CharField(max_length = 64, default = _default_string) | 	album        = models.CharField(max_length = 64, default = _default_string) | ||||||
| 	year         = models.IntegerField(default = _default_string) | 	year         = models.IntegerField(default = _default_string) | ||||||
| 	genre        = models.CharField(max_length = 64, default = _default_string) | 	genre        = models.CharField(max_length = 64, default = _default_string) | ||||||
| @ -58,7 +159,6 @@ class Song (models.Model): | |||||||
| 	bit_rate         = models.IntegerField(default = _default_int) | 	bit_rate         = models.IntegerField(default = _default_int) | ||||||
| 	duration         = models.FloatField(default = _default_int) | 	duration         = models.FloatField(default = _default_int) | ||||||
| 	add_date         = models.DateField(default = _default_date) | 	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) | 	file_size        = models.IntegerField(default = _default_int) | ||||||
| @ -79,6 +179,9 @@ class Song (models.Model): | |||||||
| 	RATING_GOOD      = _default_rating_good | 	RATING_GOOD      = _default_rating_good | ||||||
| 	RATING_EXCELLENT = _default_rating_excellent | 	RATING_EXCELLENT = _default_rating_excellent | ||||||
|  |  | ||||||
|  | 	class Meta: | ||||||
|  | 		app_label = 'archiver' | ||||||
|  |  | ||||||
| 	def _get_full_url(self): | 	def _get_full_url(self): | ||||||
| 		"Combine this song's URL with the URL of its parent" | 		"Combine this song's URL with the URL of its parent" | ||||||
| 		return os.path.join(parent_archive.root_folder, self.url) | 		return os.path.join(parent_archive.root_folder, self.url) | ||||||
| @ -111,10 +214,6 @@ class Song (models.Model): | |||||||
| 		self.file_hash = hash(file_handle.read()) | 		self.file_hash = hash(file_handle.read()) | ||||||
| 		self.file_size = os.stat(self._get_full_url).st_size | 		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): | 	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" | ||||||
| 		#Use mutagen to get the song metadata | 		#Use mutagen to get the song metadata | ||||||
| @ -145,8 +244,10 @@ class Song (models.Model): | |||||||
| 			#Couldn't grab the local data | 			#Couldn't grab the local data | ||||||
| 			return False | 			return False | ||||||
|  |  | ||||||
| 	def populate_metadata(self, use_echonest = False): | 	def populate_metadata(self): | ||||||
| 		"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. | ||||||
|  | 		""" | ||||||
| 		if self._file_not_changed(): | 		if self._file_not_changed(): | ||||||
| 			return | 			return | ||||||
|  |  | ||||||
| @ -157,6 +258,15 @@ class Song (models.Model): | |||||||
| 		else: | 		else: | ||||||
| 			self._grab_metadata_local() | 			self._grab_metadata_local() | ||||||
| 			 | 			 | ||||||
| 	def convert(self, output_location, output_format, progress_func = lambda x, y: None): | 	def convert(self, output_location, output_format): | ||||||
| 		"Convert a song to a new format." | 		""" | ||||||
|  | 		Convert a song to a new format. | ||||||
|  |  | ||||||
|  | 		:param output_location: String URL of where the resulting file should be stored | ||||||
|  | 		:param output_format: Output format of the resulting file | ||||||
|  |  | ||||||
|  | 		.. todo:: | ||||||
|  |  | ||||||
|  | 		   Actually write the code to convert files, or abandon if necessary | ||||||
|  | 		""" | ||||||
| 		pass #Need to get pydub code in place | 		pass #Need to get pydub code in place | ||||||
|  | |||||||
| @ -26,7 +26,13 @@ sys.path.insert(0, os.path.abspath('../..')) # Django project root | |||||||
|  |  | ||||||
| # Add any Sphinx extension module names here, as strings. They can be extensions | # Add any Sphinx extension module names here, as strings. They can be extensions | ||||||
| # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. | ||||||
| extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.todo'] | ||||||
|  |  | ||||||
|  | # Enable TODO support | ||||||
|  | todo_include_todos = True | ||||||
|  |  | ||||||
|  | # Document class members in source order | ||||||
|  | autodoc_member_order = 'bysource' | ||||||
|  |  | ||||||
| # Add any paths that contain templates here, relative to this directory. | # Add any paths that contain templates here, relative to this directory. | ||||||
| templates_path = ['_templates'] | templates_path = ['_templates'] | ||||||
|  | |||||||
							
								
								
									
										39
									
								
								doc/docs/archiver.models.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								doc/docs/archiver.models.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | models Package | ||||||
|  | ============== | ||||||
|  |  | ||||||
|  | :mod:`models` Package | ||||||
|  | --------------------- | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.models | ||||||
|  |     :members: | ||||||
|  |     :undoc-members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | :mod:`archive` Module | ||||||
|  | --------------------- | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.models.archive | ||||||
|  |     :members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | :mod:`feed` Module | ||||||
|  | ------------------ | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.models.feed | ||||||
|  |     :members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | :mod:`playlist` Module | ||||||
|  | ---------------------- | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.models.playlist | ||||||
|  |     :members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | :mod:`song` Module | ||||||
|  | ------------------ | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.models.song | ||||||
|  |     :members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
| @ -1,5 +1,42 @@ | |||||||
| ==== | archiver Package | ||||||
| Archive backend documentation | ================ | ||||||
| ==== |  | ||||||
|  | :mod:`archiver` Package | ||||||
|  | ----------------------- | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.__init__ | ||||||
|  |     :members: | ||||||
|  |     :undoc-members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | :mod:`listfield` Module | ||||||
|  | ----------------------- | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.listfield | ||||||
|  |     :members: | ||||||
|  |     :undoc-members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | :mod:`tests` Module | ||||||
|  | ------------------- | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.tests | ||||||
|  |     :members: | ||||||
|  |     :undoc-members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | :mod:`views` Module | ||||||
|  | ------------------- | ||||||
|  |  | ||||||
|  | .. automodule:: archiver.views | ||||||
|  |     :members: | ||||||
|  |     :undoc-members: | ||||||
|  |     :show-inheritance: | ||||||
|  |  | ||||||
|  | Subpackages | ||||||
|  | ----------- | ||||||
|  |  | ||||||
|  | .. toctree:: | ||||||
|  |  | ||||||
|  |     archiver.models | ||||||
|  |  | ||||||
| .. automodule:: archiver |  | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								doc/docs/modules.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								doc/docs/modules.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | archiver | ||||||
|  | ======== | ||||||
|  |  | ||||||
|  | .. toctree:: | ||||||
|  |    :maxdepth: 4 | ||||||
|  |  | ||||||
|  |    archiver | ||||||
		Reference in New Issue
	
	Block a user
	 Bradlee Speice
					Bradlee Speice