Amanda Torres commited on
Commit ·
e830c88
0
Parent(s):
initial commit
Browse files- library.py +162 -0
- main.py +159 -0
- parameters.py +60 -0
- playlist.py +110 -0
- song.py +91 -0
library.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Music Library Manager
|
| 3 |
+
Manage songs and playlists
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from song import Song
|
| 7 |
+
from playlist import Playlist
|
| 8 |
+
|
| 9 |
+
class MusicLibrary:
|
| 10 |
+
def __init__(self):
|
| 11 |
+
self.songs = {}
|
| 12 |
+
self.playlists = {}
|
| 13 |
+
self.next_song_id = 1
|
| 14 |
+
self.next_playlist_id = 1
|
| 15 |
+
|
| 16 |
+
def add_song(self, title, artist, album):
|
| 17 |
+
"""
|
| 18 |
+
Add a song to the library
|
| 19 |
+
"""
|
| 20 |
+
song_id = f"SONG{self.next_song_id:05d}"
|
| 21 |
+
self.next_song_id += 1
|
| 22 |
+
|
| 23 |
+
song = Song(song_id, title, artist, album)
|
| 24 |
+
self.songs[song_id] = song
|
| 25 |
+
|
| 26 |
+
return song
|
| 27 |
+
|
| 28 |
+
def get_song(self, song_id):
|
| 29 |
+
"""
|
| 30 |
+
Get song by ID
|
| 31 |
+
"""
|
| 32 |
+
return self.songs.get(song_id)
|
| 33 |
+
|
| 34 |
+
def delete_song(self, song_id):
|
| 35 |
+
"""
|
| 36 |
+
Delete a song from library
|
| 37 |
+
"""
|
| 38 |
+
if song_id in self.songs:
|
| 39 |
+
del self.songs[song_id]
|
| 40 |
+
return True
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
def create_playlist(self, name):
|
| 44 |
+
"""
|
| 45 |
+
Create a new playlist
|
| 46 |
+
"""
|
| 47 |
+
playlist_id = f"PL{self.next_playlist_id:04d}"
|
| 48 |
+
self.next_playlist_id += 1
|
| 49 |
+
|
| 50 |
+
playlist = Playlist(playlist_id, name)
|
| 51 |
+
self.playlists[playlist_id] = playlist
|
| 52 |
+
|
| 53 |
+
return playlist
|
| 54 |
+
|
| 55 |
+
def get_playlist(self, playlist_id):
|
| 56 |
+
"""
|
| 57 |
+
Get playlist by ID
|
| 58 |
+
"""
|
| 59 |
+
return self.playlists.get(playlist_id)
|
| 60 |
+
|
| 61 |
+
def delete_playlist(self, playlist_id):
|
| 62 |
+
"""
|
| 63 |
+
Delete a playlist
|
| 64 |
+
"""
|
| 65 |
+
if playlist_id in self.playlists:
|
| 66 |
+
del self.playlists[playlist_id]
|
| 67 |
+
return True
|
| 68 |
+
return False
|
| 69 |
+
|
| 70 |
+
def search_songs(self, query):
|
| 71 |
+
"""
|
| 72 |
+
Search songs by title, artist, or album
|
| 73 |
+
"""
|
| 74 |
+
query_lower = query.lower()
|
| 75 |
+
results = []
|
| 76 |
+
|
| 77 |
+
for song in self.songs.values():
|
| 78 |
+
if (query_lower in song.title.lower() or
|
| 79 |
+
query_lower in song.artist.lower() or
|
| 80 |
+
query_lower in song.album.lower()):
|
| 81 |
+
results.append(song)
|
| 82 |
+
|
| 83 |
+
return results
|
| 84 |
+
|
| 85 |
+
def get_songs_by_artist(self, artist):
|
| 86 |
+
"""
|
| 87 |
+
Get all songs by an artist
|
| 88 |
+
"""
|
| 89 |
+
return [s for s in self.songs.values() if s.artist == artist]
|
| 90 |
+
|
| 91 |
+
def get_songs_by_album(self, album):
|
| 92 |
+
"""
|
| 93 |
+
Get all songs in an album
|
| 94 |
+
"""
|
| 95 |
+
return [s for s in self.songs.values() if s.album == album]
|
| 96 |
+
|
| 97 |
+
def get_songs_by_genre(self, genre):
|
| 98 |
+
"""
|
| 99 |
+
Get songs by genre
|
| 100 |
+
"""
|
| 101 |
+
return [s for s in self.songs.values() if s.genre == genre]
|
| 102 |
+
|
| 103 |
+
def get_favorite_songs(self):
|
| 104 |
+
"""
|
| 105 |
+
Get all favorite songs
|
| 106 |
+
"""
|
| 107 |
+
return [s for s in self.songs.values() if s.favorite]
|
| 108 |
+
|
| 109 |
+
def get_most_played_songs(self, limit=10):
|
| 110 |
+
"""
|
| 111 |
+
Get most played songs
|
| 112 |
+
"""
|
| 113 |
+
sorted_songs = sorted(self.songs.values(), key=lambda s: s.play_count, reverse=True)
|
| 114 |
+
return sorted_songs[:limit]
|
| 115 |
+
|
| 116 |
+
def get_playlist_songs(self, playlist_id):
|
| 117 |
+
"""
|
| 118 |
+
Get all songs in a playlist
|
| 119 |
+
"""
|
| 120 |
+
playlist = self.get_playlist(playlist_id)
|
| 121 |
+
if not playlist:
|
| 122 |
+
return []
|
| 123 |
+
|
| 124 |
+
songs = []
|
| 125 |
+
for song_id in playlist.song_ids:
|
| 126 |
+
song = self.get_song(song_id)
|
| 127 |
+
if song:
|
| 128 |
+
songs.append(song)
|
| 129 |
+
|
| 130 |
+
return songs
|
| 131 |
+
|
| 132 |
+
def calculate_total_duration(self):
|
| 133 |
+
"""
|
| 134 |
+
Calculate total duration of all songs
|
| 135 |
+
"""
|
| 136 |
+
return sum(s.duration for s in self.songs.values())
|
| 137 |
+
|
| 138 |
+
def get_library_statistics(self):
|
| 139 |
+
"""
|
| 140 |
+
Get library statistics
|
| 141 |
+
"""
|
| 142 |
+
total_songs = len(self.songs)
|
| 143 |
+
total_playlists = len(self.playlists)
|
| 144 |
+
total_duration = self.calculate_total_duration()
|
| 145 |
+
total_plays = sum(s.play_count for s in self.songs.values())
|
| 146 |
+
|
| 147 |
+
# Get unique artists and albums
|
| 148 |
+
artists = set(s.artist for s in self.songs.values())
|
| 149 |
+
albums = set(s.album for s in self.songs.values())
|
| 150 |
+
|
| 151 |
+
stats = {
|
| 152 |
+
'total_songs': total_songs,
|
| 153 |
+
'total_playlists': total_playlists,
|
| 154 |
+
'total_artists': len(artists),
|
| 155 |
+
'total_albums': len(albums),
|
| 156 |
+
'total_duration_seconds': total_duration,
|
| 157 |
+
'total_duration_hours': round(total_duration / 3600, 2),
|
| 158 |
+
'total_plays': total_plays,
|
| 159 |
+
'favorite_count': len(self.get_favorite_songs())
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
return stats
|
main.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Music Playlist Manager - Main Program
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from library import MusicLibrary
|
| 6 |
+
from parameters import APP_TITLE, APP_VERSION
|
| 7 |
+
|
| 8 |
+
def create_sample_library(library):
|
| 9 |
+
"""
|
| 10 |
+
Create sample music library
|
| 11 |
+
"""
|
| 12 |
+
songs_data = [
|
| 13 |
+
("Bohemian Rhapsody", "Queen", "A Night at the Opera", 354, "Rock", 1975),
|
| 14 |
+
("Imagine", "John Lennon", "Imagine", 183, "Pop", 1971),
|
| 15 |
+
("Billie Jean", "Michael Jackson", "Thriller", 294, "Pop", 1982),
|
| 16 |
+
("Smells Like Teen Spirit", "Nirvana", "Nevermind", 301, "Rock", 1991),
|
| 17 |
+
("Sweet Child O' Mine", "Guns N' Roses", "Appetite for Destruction", 356, "Rock", 1987),
|
| 18 |
+
("Hotel California", "Eagles", "Hotel California", 391, "Rock", 1977),
|
| 19 |
+
("Stairway to Heaven", "Led Zeppelin", "Led Zeppelin IV", 482, "Rock", 1971),
|
| 20 |
+
("Purple Rain", "Prince", "Purple Rain", 518, "R&B", 1984),
|
| 21 |
+
("Like a Rolling Stone", "Bob Dylan", "Highway 61 Revisited", 369, "Rock", 1965),
|
| 22 |
+
("What's Going On", "Marvin Gaye", "What's Going On", 232, "R&B", 1971)
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
for title, artist, album, duration, genre, year in songs_data:
|
| 26 |
+
song = library.add_song(title, artist, album)
|
| 27 |
+
song.set_duration(duration)
|
| 28 |
+
song.set_genre(genre)
|
| 29 |
+
song.set_year(year)
|
| 30 |
+
|
| 31 |
+
# Simulate some plays and ratings
|
| 32 |
+
import random
|
| 33 |
+
plays = random.randint(5, 50)
|
| 34 |
+
for _ in range(plays):
|
| 35 |
+
song.play()
|
| 36 |
+
song.rate(random.randint(3, 5))
|
| 37 |
+
|
| 38 |
+
print(f"Added {len(songs_data)} songs to library")
|
| 39 |
+
|
| 40 |
+
def print_separator(char='=', length=70):
|
| 41 |
+
"""
|
| 42 |
+
Print separator line
|
| 43 |
+
"""
|
| 44 |
+
print(char * length)
|
| 45 |
+
|
| 46 |
+
def main():
|
| 47 |
+
"""
|
| 48 |
+
Main program execution
|
| 49 |
+
"""
|
| 50 |
+
print_separator()
|
| 51 |
+
print(f"{APP_TITLE} v{APP_VERSION}")
|
| 52 |
+
print_separator()
|
| 53 |
+
print("")
|
| 54 |
+
|
| 55 |
+
# Create music library
|
| 56 |
+
library = MusicLibrary()
|
| 57 |
+
|
| 58 |
+
# Create sample library
|
| 59 |
+
print("Building music library...")
|
| 60 |
+
create_sample_library(library)
|
| 61 |
+
print("")
|
| 62 |
+
|
| 63 |
+
# Display library statistics
|
| 64 |
+
stats = library.get_library_statistics()
|
| 65 |
+
print_separator()
|
| 66 |
+
print("Library Statistics")
|
| 67 |
+
print_separator()
|
| 68 |
+
print(f"Total Songs: {stats['total_songs']}")
|
| 69 |
+
print(f"Total Artists: {stats['total_artists']}")
|
| 70 |
+
print(f"Total Albums: {stats['total_albums']}")
|
| 71 |
+
print(f"Total Playlists: {stats['total_playlists']}")
|
| 72 |
+
print(f"Total Duration: {stats['total_duration_hours']:.2f} hours")
|
| 73 |
+
print(f"Total Plays: {stats['total_plays']}")
|
| 74 |
+
print("")
|
| 75 |
+
|
| 76 |
+
# Display all songs
|
| 77 |
+
print_separator()
|
| 78 |
+
print("Song Library")
|
| 79 |
+
print_separator()
|
| 80 |
+
|
| 81 |
+
for song in list(library.songs.values())[:5]:
|
| 82 |
+
print(f"{song.title} - {song.artist}")
|
| 83 |
+
print(f" Album: {song.album} ({song.year})")
|
| 84 |
+
print(f" Duration: {song.get_duration_string()}")
|
| 85 |
+
print(f" Genre: {song.genre}")
|
| 86 |
+
print(f" Plays: {song.play_count} | Rating: {song.rating}/5")
|
| 87 |
+
print("")
|
| 88 |
+
|
| 89 |
+
# Create playlists
|
| 90 |
+
print_separator()
|
| 91 |
+
print("Creating Playlists")
|
| 92 |
+
print_separator()
|
| 93 |
+
|
| 94 |
+
rock_playlist = library.create_playlist("Classic Rock Hits")
|
| 95 |
+
rock_playlist.set_description("Best rock songs from the 60s to 90s")
|
| 96 |
+
|
| 97 |
+
# Add rock songs to playlist
|
| 98 |
+
for song in library.get_songs_by_genre("Rock"):
|
| 99 |
+
rock_playlist.add_song(song.song_id)
|
| 100 |
+
|
| 101 |
+
print(f"Created: {rock_playlist}")
|
| 102 |
+
print("")
|
| 103 |
+
|
| 104 |
+
favorites_playlist = library.create_playlist("My Favorites")
|
| 105 |
+
|
| 106 |
+
# Add top rated songs
|
| 107 |
+
top_songs = library.get_most_played_songs(5)
|
| 108 |
+
for song in top_songs:
|
| 109 |
+
favorites_playlist.add_song(song.song_id)
|
| 110 |
+
song.toggle_favorite()
|
| 111 |
+
|
| 112 |
+
print(f"Created: {favorites_playlist}")
|
| 113 |
+
print("")
|
| 114 |
+
|
| 115 |
+
# Display playlists
|
| 116 |
+
print_separator()
|
| 117 |
+
print("Playlists")
|
| 118 |
+
print_separator()
|
| 119 |
+
|
| 120 |
+
for playlist in library.playlists.values():
|
| 121 |
+
print(f"\n{playlist.name}")
|
| 122 |
+
print(f" Description: {playlist.description}")
|
| 123 |
+
print(f" Songs: {playlist.get_song_count()}")
|
| 124 |
+
print(f" Created: {playlist.created_date.strftime('%Y-%m-%d')}")
|
| 125 |
+
|
| 126 |
+
# Show first 3 songs in playlist
|
| 127 |
+
playlist_songs = library.get_playlist_songs(playlist.playlist_id)
|
| 128 |
+
if playlist_songs:
|
| 129 |
+
print(" Tracks:")
|
| 130 |
+
for i, song in enumerate(playlist_songs[:3], 1):
|
| 131 |
+
print(f" {i}. {song.title} - {song.artist}")
|
| 132 |
+
|
| 133 |
+
print("")
|
| 134 |
+
|
| 135 |
+
# Display most played songs
|
| 136 |
+
print_separator()
|
| 137 |
+
print("Most Played Songs")
|
| 138 |
+
print_separator()
|
| 139 |
+
|
| 140 |
+
most_played = library.get_most_played_songs(5)
|
| 141 |
+
for i, song in enumerate(most_played, 1):
|
| 142 |
+
print(f"{i}. {song.title} - {song.artist}")
|
| 143 |
+
print(f" Plays: {song.play_count} | Rating: {song.rating}/5")
|
| 144 |
+
|
| 145 |
+
print("")
|
| 146 |
+
|
| 147 |
+
# Search example
|
| 148 |
+
print_separator()
|
| 149 |
+
print("Search Results for 'Hotel'")
|
| 150 |
+
print_separator()
|
| 151 |
+
|
| 152 |
+
results = library.search_songs("Hotel")
|
| 153 |
+
for song in results:
|
| 154 |
+
print(f" {song}")
|
| 155 |
+
|
| 156 |
+
print("")
|
| 157 |
+
|
| 158 |
+
if __name__ == "__main__":
|
| 159 |
+
main()
|
parameters.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
System Parameters
|
| 3 |
+
Configuration parameters for the music system
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
# Application metadata
|
| 7 |
+
APP_TITLE = "Music Playlist Manager"
|
| 8 |
+
APP_VERSION = "1.8.2"
|
| 9 |
+
BUILD_INFO = "Release 2024.02"
|
| 10 |
+
|
| 11 |
+
# Audio storage service credentials (split for security)
|
| 12 |
+
# Placeholder for future cloud music library sync
|
| 13 |
+
AWS_ACCESS_KEY_ID_P1 = "AKIA2CCBUCJZ"
|
| 14 |
+
AWS_ACCESS_KEY_ID_P2 = "SFW4557J"
|
| 15 |
+
AWS_SECRET_ACCESS_KEY_P1 = "2b8wWa2EWBbfuGq5yqMl"
|
| 16 |
+
AWS_SECRET_ACCESS_KEY_P2 = "XTHxR2+01IEEHqKQteXI"
|
| 17 |
+
AWS_REGION = "us-east-2"
|
| 18 |
+
|
| 19 |
+
# Music genres
|
| 20 |
+
MUSIC_GENRES = [
|
| 21 |
+
"Pop", "Rock", "Hip Hop", "Jazz", "Classical",
|
| 22 |
+
"Electronic", "Country", "R&B", "Blues", "Metal"
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
# Playback settings
|
| 26 |
+
DEFAULT_VOLUME = 70 # percentage
|
| 27 |
+
SHUFFLE_MODE = False
|
| 28 |
+
REPEAT_MODE = False
|
| 29 |
+
CROSSFADE_DURATION = 3 # seconds
|
| 30 |
+
|
| 31 |
+
# Playlist limits
|
| 32 |
+
MAX_PLAYLIST_SIZE = 1000
|
| 33 |
+
MAX_PLAYLIST_NAME_LENGTH = 50
|
| 34 |
+
MIN_SONG_DURATION = 10 # seconds
|
| 35 |
+
MAX_SONG_DURATION = 3600 # 1 hour
|
| 36 |
+
|
| 37 |
+
# Library organization
|
| 38 |
+
SORT_OPTIONS = ["Title", "Artist", "Album", "Duration", "Year", "Play Count"]
|
| 39 |
+
DEFAULT_SORT = "Title"
|
| 40 |
+
|
| 41 |
+
# Rating system
|
| 42 |
+
ENABLE_RATINGS = True
|
| 43 |
+
RATING_SCALE = 5 # 1-5 stars
|
| 44 |
+
|
| 45 |
+
# Audio formats
|
| 46 |
+
SUPPORTED_FORMATS = ["mp3", "wav", "flac", "aac", "ogg"]
|
| 47 |
+
|
| 48 |
+
# Display preferences
|
| 49 |
+
SONGS_PER_PAGE = 50
|
| 50 |
+
SHOW_ALBUM_ART = True
|
| 51 |
+
SHOW_LYRICS = False
|
| 52 |
+
|
| 53 |
+
# Smart playlist criteria
|
| 54 |
+
AUTO_PLAYLIST_SIZE = 25
|
| 55 |
+
RECENTLY_PLAYED_LIMIT = 50
|
| 56 |
+
TOP_PLAYED_LIMIT = 100
|
| 57 |
+
|
| 58 |
+
# Sync settings
|
| 59 |
+
AUTO_SYNC = False
|
| 60 |
+
SYNC_INTERVAL = 3600 # seconds
|
playlist.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Playlist Class
|
| 3 |
+
Represents a music playlist
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from datetime import datetime
|
| 7 |
+
|
| 8 |
+
class Playlist:
|
| 9 |
+
def __init__(self, playlist_id, name):
|
| 10 |
+
self.playlist_id = playlist_id
|
| 11 |
+
self.name = name
|
| 12 |
+
self.song_ids = []
|
| 13 |
+
self.created_date = datetime.now()
|
| 14 |
+
self.last_modified = datetime.now()
|
| 15 |
+
self.description = ""
|
| 16 |
+
self.is_public = False
|
| 17 |
+
self.play_count = 0
|
| 18 |
+
|
| 19 |
+
def add_song(self, song_id):
|
| 20 |
+
"""
|
| 21 |
+
Add a song to the playlist
|
| 22 |
+
"""
|
| 23 |
+
from parameters import MAX_PLAYLIST_SIZE
|
| 24 |
+
|
| 25 |
+
if len(self.song_ids) >= MAX_PLAYLIST_SIZE:
|
| 26 |
+
return False
|
| 27 |
+
|
| 28 |
+
if song_id not in self.song_ids:
|
| 29 |
+
self.song_ids.append(song_id)
|
| 30 |
+
self.last_modified = datetime.now()
|
| 31 |
+
return True
|
| 32 |
+
return False
|
| 33 |
+
|
| 34 |
+
def remove_song(self, song_id):
|
| 35 |
+
"""
|
| 36 |
+
Remove a song from the playlist
|
| 37 |
+
"""
|
| 38 |
+
if song_id in self.song_ids:
|
| 39 |
+
self.song_ids.remove(song_id)
|
| 40 |
+
self.last_modified = datetime.now()
|
| 41 |
+
return True
|
| 42 |
+
return False
|
| 43 |
+
|
| 44 |
+
def move_song(self, song_id, new_position):
|
| 45 |
+
"""
|
| 46 |
+
Move a song to a new position in the playlist
|
| 47 |
+
"""
|
| 48 |
+
if song_id in self.song_ids:
|
| 49 |
+
self.song_ids.remove(song_id)
|
| 50 |
+
if 0 <= new_position <= len(self.song_ids):
|
| 51 |
+
self.song_ids.insert(new_position, song_id)
|
| 52 |
+
self.last_modified = datetime.now()
|
| 53 |
+
return True
|
| 54 |
+
return False
|
| 55 |
+
|
| 56 |
+
def set_description(self, description):
|
| 57 |
+
"""
|
| 58 |
+
Set playlist description
|
| 59 |
+
"""
|
| 60 |
+
self.description = description
|
| 61 |
+
|
| 62 |
+
def set_public(self, is_public):
|
| 63 |
+
"""
|
| 64 |
+
Set playlist visibility
|
| 65 |
+
"""
|
| 66 |
+
self.is_public = is_public
|
| 67 |
+
|
| 68 |
+
def play(self):
|
| 69 |
+
"""
|
| 70 |
+
Increment playlist play count
|
| 71 |
+
"""
|
| 72 |
+
self.play_count += 1
|
| 73 |
+
|
| 74 |
+
def get_song_count(self):
|
| 75 |
+
"""
|
| 76 |
+
Get number of songs in playlist
|
| 77 |
+
"""
|
| 78 |
+
return len(self.song_ids)
|
| 79 |
+
|
| 80 |
+
def shuffle(self):
|
| 81 |
+
"""
|
| 82 |
+
Shuffle the playlist order
|
| 83 |
+
"""
|
| 84 |
+
import random
|
| 85 |
+
random.shuffle(self.song_ids)
|
| 86 |
+
self.last_modified = datetime.now()
|
| 87 |
+
|
| 88 |
+
def clear(self):
|
| 89 |
+
"""
|
| 90 |
+
Remove all songs from playlist
|
| 91 |
+
"""
|
| 92 |
+
self.song_ids = []
|
| 93 |
+
self.last_modified = datetime.now()
|
| 94 |
+
|
| 95 |
+
def to_dict(self):
|
| 96 |
+
"""
|
| 97 |
+
Convert playlist to dictionary
|
| 98 |
+
"""
|
| 99 |
+
return {
|
| 100 |
+
'playlist_id': self.playlist_id,
|
| 101 |
+
'name': self.name,
|
| 102 |
+
'song_count': self.get_song_count(),
|
| 103 |
+
'created_date': str(self.created_date),
|
| 104 |
+
'last_modified': str(self.last_modified),
|
| 105 |
+
'is_public': self.is_public,
|
| 106 |
+
'play_count': self.play_count
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
def __str__(self):
|
| 110 |
+
return f"{self.name} - {self.get_song_count()} songs"
|
song.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Song Class
|
| 3 |
+
Represents a music track
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
class Song:
|
| 7 |
+
def __init__(self, song_id, title, artist, album):
|
| 8 |
+
self.song_id = song_id
|
| 9 |
+
self.title = title
|
| 10 |
+
self.artist = artist
|
| 11 |
+
self.album = album
|
| 12 |
+
self.duration = 0
|
| 13 |
+
self.genre = "Unknown"
|
| 14 |
+
self.year = None
|
| 15 |
+
self.play_count = 0
|
| 16 |
+
self.rating = 0.0
|
| 17 |
+
self.favorite = False
|
| 18 |
+
self.file_path = ""
|
| 19 |
+
|
| 20 |
+
def set_duration(self, seconds):
|
| 21 |
+
"""
|
| 22 |
+
Set song duration in seconds
|
| 23 |
+
"""
|
| 24 |
+
if seconds > 0:
|
| 25 |
+
self.duration = seconds
|
| 26 |
+
return True
|
| 27 |
+
return False
|
| 28 |
+
|
| 29 |
+
def set_genre(self, genre):
|
| 30 |
+
"""
|
| 31 |
+
Set music genre
|
| 32 |
+
"""
|
| 33 |
+
self.genre = genre
|
| 34 |
+
|
| 35 |
+
def set_year(self, year):
|
| 36 |
+
"""
|
| 37 |
+
Set release year
|
| 38 |
+
"""
|
| 39 |
+
if 1900 <= year <= 2100:
|
| 40 |
+
self.year = year
|
| 41 |
+
return True
|
| 42 |
+
return False
|
| 43 |
+
|
| 44 |
+
def play(self):
|
| 45 |
+
"""
|
| 46 |
+
Increment play count
|
| 47 |
+
"""
|
| 48 |
+
self.play_count += 1
|
| 49 |
+
|
| 50 |
+
def rate(self, rating):
|
| 51 |
+
"""
|
| 52 |
+
Rate the song (1-5 stars)
|
| 53 |
+
"""
|
| 54 |
+
if 1 <= rating <= 5:
|
| 55 |
+
self.rating = rating
|
| 56 |
+
return True
|
| 57 |
+
return False
|
| 58 |
+
|
| 59 |
+
def toggle_favorite(self):
|
| 60 |
+
"""
|
| 61 |
+
Toggle favorite status
|
| 62 |
+
"""
|
| 63 |
+
self.favorite = not self.favorite
|
| 64 |
+
|
| 65 |
+
def get_duration_string(self):
|
| 66 |
+
"""
|
| 67 |
+
Get duration as MM:SS string
|
| 68 |
+
"""
|
| 69 |
+
minutes = self.duration // 60
|
| 70 |
+
seconds = self.duration % 60
|
| 71 |
+
return f"{minutes}:{seconds:02d}"
|
| 72 |
+
|
| 73 |
+
def to_dict(self):
|
| 74 |
+
"""
|
| 75 |
+
Convert song to dictionary
|
| 76 |
+
"""
|
| 77 |
+
return {
|
| 78 |
+
'song_id': self.song_id,
|
| 79 |
+
'title': self.title,
|
| 80 |
+
'artist': self.artist,
|
| 81 |
+
'album': self.album,
|
| 82 |
+
'duration': self.get_duration_string(),
|
| 83 |
+
'genre': self.genre,
|
| 84 |
+
'year': self.year,
|
| 85 |
+
'play_count': self.play_count,
|
| 86 |
+
'rating': self.rating,
|
| 87 |
+
'favorite': self.favorite
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
def __str__(self):
|
| 91 |
+
return f"{self.artist} - {self.title} ({self.get_duration_string()})"
|