# Path: music/abstract_source.py
# class AbstractSource:
#     """
#     An abstract class for an audio source.
#     """
#     __slots__ = ('__detail', '__get_detail')
# 
#     def __init__(self, __get_detail: callable):
#         self.__detail = None
#         self.__get_detail = __get_detail
# 
#     def __str__(self):
#         raise NotImplementedError
# 
#     def clean_up(self):
#         raise NotImplementedError
# 
#     @property
#     def detail(self):
#         if not self.__detail:
#             self.__detail = self.__get_detail()
#             del self.__get_detail
#         return self.__detail
# 
#     async def true_name(self) -> str:
#         """
#         :return: Name used by `FFmpegPCMAudio`
#         """
#         raise NotImplementedError
# 
# Path: music/music_util.py
# def file_detail(title, genre, artist, album, length) -> str:
#     """
#     :return: a detailed string repersentation of a file audio source.
#     """
#     artist = f'\nArtist:\n`{artist}`' if artist else ''
#     album = f'\nAlbum:\n`{album}\n`' if album else ''
#     genre = f'\nGenre:\n`{genre}`' if genre else ''
#     length = f' [{length}]' if length else ''
#     return f'\t{title}{length}\n{artist}\n{album}\n{genre}'
# 
# def get_file_info(file_path: str) -> tuple:
#     """
#     Get mp3/mp4/flac tags from a file.
#     :param file_path: the file path.
#     :return: a tuple of  (title, genre, artist, album, length)
#     """
#     empty = lambda: (Path(file_path).name, None, None, None, None)
#     tag = None
#     try:
#         if file_path.endswith('.flac'):
#             tag = FLAC(file_path)
#         elif file_path.endswith('.mp3'):
#             tag = EasyMP3(file_path)
#         elif file_path.endswith('m4a'):
#             tag = EasyMP4(file_path)
#         else:
#             return empty()
#         get = lambda t, s: t.get(s, None) or t.get(s.upper(), None)
#         title = get(tag, 'title') or Path(file_path).name
#         genre = get(tag, 'genre')
#         artist = get(tag, 'artist')
#         album = get(tag, 'album')
#         length = tag.info.length
#         if isinstance(length, (int, float)):
#             minutes, seconds = divmod(length, 60)
#             length_str = f'{int(minutes)}:{round(seconds):02d}'
#         else:
#             length_str = None
#         return (__to_string(title), __to_string(genre),
#                 __to_string(artist), __to_string(album), length_str)
#     except MutagenError:
#         return empty()
#     finally:
#         del tag


from functools import partial
from music.abstract_source import AbstractSource
from music.music_util import file_detail, get_file_info



class FileSource(AbstractSource):
    """
    An audio source from a file.
    """

    __slots__ = ('file_path', 'title')

    def __init__(self, file_path: str):
        """
        :param file_path: the file path.
        """
        self.title, genre, artist, album, length = get_file_info(file_path)
        self.file_path = file_path
        super().__init__(

# Path: music/abstract_source.py
# class AbstractSource:
#     """
#     An abstract class for an audio source.
#     """
#     __slots__ = ('__detail', '__get_detail')
# 
#     def __init__(self, __get_detail: callable):
#         self.__detail = None
#         self.__get_detail = __get_detail
# 
#     def __str__(self):
#         raise NotImplementedError
# 
#     def clean_up(self):
#         raise NotImplementedError
# 
#     @property
#     def detail(self):
#         if not self.__detail:
#             self.__detail = self.__get_detail()
#             del self.__get_detail
#         return self.__detail
# 
#     async def true_name(self) -> str:
#         """
#         :return: Name used by `FFmpegPCMAudio`
#         """
#         raise NotImplementedError
# 
# Path: music/music_util.py
# def file_detail(title, genre, artist, album, length) -> str:
#     """
#     :return: a detailed string repersentation of a file audio source.
#     """
#     artist = f'\nArtist:\n`{artist}`' if artist else ''
#     album = f'\nAlbum:\n`{album}\n`' if album else ''
#     genre = f'\nGenre:\n`{genre}`' if genre else ''
#     length = f' [{length}]' if length else ''
#     return f'\t{title}{length}\n{artist}\n{album}\n{genre}'
# 
# def get_file_info(file_path: str) -> tuple:
#     """
#     Get mp3/mp4/flac tags from a file.
#     :param file_path: the file path.
#     :return: a tuple of  (title, genre, artist, album, length)
#     """
#     empty = lambda: (Path(file_path).name, None, None, None, None)
#     tag = None
#     try:
#         if file_path.endswith('.flac'):
#             tag = FLAC(file_path)
#         elif file_path.endswith('.mp3'):
#             tag = EasyMP3(file_path)
#         elif file_path.endswith('m4a'):
#             tag = EasyMP4(file_path)
#         else:
#             return empty()
#         get = lambda t, s: t.get(s, None) or t.get(s.upper(), None)
#         title = get(tag, 'title') or Path(file_path).name
#         genre = get(tag, 'genre')
#         artist = get(tag, 'artist')
#         album = get(tag, 'album')
#         length = tag.info.length
#         if isinstance(length, (int, float)):
#             minutes, seconds = divmod(length, 60)
#             length_str = f'{int(minutes)}:{round(seconds):02d}'
#         else:
#             length_str = None
#         return (__to_string(title), __to_string(genre),
#                 __to_string(artist), __to_string(album), length_str)
#     except MutagenError:
#         return empty()
#     finally:
#         del tag


# Path: music/file_source.py
from functools import partial
from music.abstract_source import AbstractSource
from music.music_util import file_detail, get_file_info




class FileSource(AbstractSource):
    """
    An audio source from a file.
    """

    __slots__ = ('file_path', 'title')

    def __init__(self, file_path: str):
        """
        :param file_path: the file path.
        """
        self.title, genre, artist, album, length = get_file_info(file_path)
        self.file_path = file_path
        super().__init__(

            partial(file_detail, self.title, genre, artist, album, length)