# -*- coding: utf-8 -*-
"""Models the domain objects.
Defines classes for Song, Album and Discography.
"""
import os
from codecs import open
from .utils import set_save_folder, normalize
[docs]class Song(object):
"""
Song class.
:param title: string.
Song title.
:param album: string.
Album title.
:param artist: string.
Author name.
:param lyrics: string.
Lyrics of the song.
:param writers: string.
List of the song's writers.
"""
__slots__ = ('title', 'album', 'artist', 'lyrics', 'writers')
def __init__(self, title, album, artist, lyrics=None, writers=None):
self.title = title
self.album = album
self.artist = artist
self.lyrics = lyrics
self.writers = writers
def __repr__(self):
return '{0}.{1}({2}, {3}, {4})'.format(__name__, self.__class__.__name__, self.title, self.album, self.artist)
[docs] def save(self, folder=None):
"""
Saves the lyrics of the song in the supplied folder.
If no folder is supplied, 'folder' is set to {user}/Documents/lyricsmaster/
The lyrics of a song are saved in the folder /artist/album/song_title.txt
:param folder: string.
path to save folder.
"""
folder = set_save_folder(folder)
if self.lyrics:
artist = normalize(self.artist)
album = normalize(self.album)
save_path = os.path.join(folder, artist, album)
if not os.path.exists(save_path):
os.makedirs(save_path)
file_name = normalize(self.title)
with open(os.path.join(save_path, file_name + ".txt"), "w", encoding="utf-8") as file:
file.write(self.lyrics)
[docs]class Album(object):
"""
Album Class.
The Album class follows the Iterable protocol and can be iterated over the songs.
:param title: string.
Album title.
:param artist: string.
Artist name.
:param release_date: string.
Release date.
:param songs: list.
List of Songs objects.
"""
__slots__ = ('__idx__', 'title', 'artist', 'release_date', 'songs')
def __init__(self, title, artist, songs, release_date='Unknown'):
self.__idx__ = 0
self.title = title
self.artist = artist
self.release_date = release_date
self.songs = songs
def __repr__(self):
return '{0}.{1}({2}, {3})'.format(__name__, self.__class__.__name__, self.title, self.artist)
def __len__(self):
return len(self.songs)
def __getitem__(self, key):
return self.songs[key]
def __setitem__(self, key, value):
if not isinstance(value, Song):
raise TypeError
else:
self.songs[key] = value
return
def __delitem__(self, key):
del self.songs[key]
return
def __iter__(self):
return self
def __next__(self):
self.__idx__ += 1
try:
return self.songs[self.__idx__ - 1]
except IndexError:
self.__idx__ = 0
raise StopIteration
def __reversed__(self):
return reversed(self.songs)
next = __next__ # Python 2.7 compatibility for iterator protocol
[docs] def save(self, folder=None):
"""
Saves the album in the supplied folder.
:param folder: string.
path to save folder.
"""
for song in self.songs:
if song:
song.save(folder)
[docs]class Discography(object):
"""
Discography Class.
The Discography class follows the Iterable protocol and can be iterated over the albums.
:param artist: string.
Artist name.
:param albums: list.
List of Album objects.
"""
__slots__ = ('__idx__', 'artist', 'albums')
def __init__(self, artist, albums):
self.__idx__ = 0
self.artist = artist
self.albums = albums
def __repr__(self):
return '{0}.{1}({2})'.format(__name__, self.__class__.__name__, self.artist)
def __len__(self):
return len(self.albums)
def __getitem__(self, key):
return self.albums[key]
def __setitem__(self, key, value):
if not isinstance(value, Song):
raise TypeError
else:
self.albums[key] = value
return
def __delitem__(self, key):
del self.albums[key]
return
def __iter__(self):
return self
def __next__(self):
self.__idx__ += 1
try:
return self.albums[self.__idx__ - 1]
except IndexError:
self.__idx__ = 0
raise StopIteration
def __reversed__(self):
return reversed(self.albums)
next = __next__ # Python 2.7 compatibility for iterator protocol
[docs] def save(self, folder=None):
"""
Saves Discography in the supplied folder.
:param folder: string.
Path to save folder.
"""
for album in self.albums:
album.save(folder)