# Path: fuocore/provider.py
# class AbstractProvider(ABC):
#     """abstract music resource provider
#     """
# 
#     # A well behaved provider should implement its own models .
#     Song = SongModel
#     Artist = ArtistModel
#     Album = AlbumModel
#     Playlist = PlaylistModel
#     Lyric = LyricModel
#     User = UserModel
# 
#     def __init__(self):
#         self._user = None
# 
# 
#     def get_model_cls(self, model_type):
#         name = _TYPE_NAME_MAP[model_type]
#         return getattr(self, name)
# 
#     def set_model_cls(self, model_type, model_cls):
#         name = _TYPE_NAME_MAP[model_type]
#         setattr(self, name, model_cls)
# 
#     @property
#     @abstractmethod
#     def identifier(self):
#         """provider identify"""
# 
#     @property
#     @abstractmethod
#     def name(self):
#         """provider name"""
# 
#     @contextmanager
#     def auth_as(self, user):
#         """auth as a user temporarily"""
#         old_user = self._user
#         self.auth(user)
#         try:
#             yield
#         finally:
#             self.auth(old_user)
# 
#     def auth(self, user):
#         """use provider as a specific user"""
#         self._user = user
# 
# Path: fuocore/utils.py
# def elfhash(s):
#     """
#     :param string: bytes
# 
#     >>> import base64
#     >>> s = base64.b64encode(b'hello world')
#     >>> elfhash(s)
#     224648685
#     """
#     hash = 0
#     x = 0
#     for c in s:
#         hash = (hash << 4) + c
#         x = hash & 0xF0000000
#         if x:
#             hash ^= (x >> 24)
#             hash &= ~x
#     return (hash & 0x7FFFFFFF)
# 
# Path: fuocore/utils.py
# def log_exectime(func):
#     @wraps(func)
#     def wrapper(*args, **kwargs):
#         t = time.process_time()
#         result = func(*args, **kwargs)
#         elapsed_time = time.process_time() - t
#         logger.debug('function %s executed time: %f ms',
#                      func.__name__, elapsed_time * 1000)
#         return result
#     return wrapper
# 
# Path: fuocore/local/schemas.py
# class EasyMP3MetadataSongSchema(Schema):
#     """EasyMP3 metadata"""
#     url = fields.Str(required=True)
#     duration = fields.Float(required=True)
#     title = fields.Str(required=True, missing=DEFAULT_TITLE)
#     artists_name = fields.Str(required=True, load_from='artist',
#                               missing=DEFAULT_ARTIST_NAME)
#     album_name = fields.Str(required=True, load_from='album',
#                             missing=DEFAULT_ALBUM_NAME)
#     album_artist_name = fields.Str(required=True, load_from='albumartist',
#                                    missing=DEFAULT_ARTIST_NAME)
#     track = fields.Str(load_from='tracknumber', missing='1/1')
#     disc = fields.Str(load_from='discnumber', missing='1/1')
#     date = fields.Str(missing='')
#     genre = fields.Str(missing='')
# 
#     # 本地歌曲目前都不支持描述和封面，将它们设置为空字符串
#     desc = fields.Str(missing='')
#     cover = fields.Str(missing='')
# 
# Path: fuocore/local/models.py
# class LSearchModel(SearchModel, LBaseModel):
#     pass
# 
# class LSongModel(SongModel, LBaseModel):
#     class Meta:
#         fields = ('disc', 'genre', 'date', 'track', 'cover', 'desc')
#         fields_no_get = ('lyric', )
# 
#     @classmethod
#     def get(cls, identifier):
#         return cls.meta.provider.library.get_song(identifier)
# 
#     @classmethod
#     def list(cls, identifier_list):
#         return map(cls.meta.provider.library._songs.get, identifier_list)
# 
# class LAlbumModel(AlbumModel, LBaseModel):
#     _detail_fields = ('songs',)
# 
#     @classmethod
#     def get(cls, identifier):
#         return cls.meta.provider.library.get_album(identifier)
# 
# class LArtistModel(ArtistModel, LBaseModel):
#     _detail_fields = ('songs',)
# 
#     @classmethod
#     def get(cls, identifier):
#         return cls.meta.provider.library.get_artist(identifier)


import base64
import logging
import os
import re
from fuzzywuzzy import process
from marshmallow.exceptions import ValidationError
from mutagen import MutagenError
from mutagen.mp3 import EasyMP3
from mutagen.easymp4 import EasyMP4
from fuocore.provider import AbstractProvider
from fuocore.utils import elfhash
from fuocore.utils import log_exectime
from .schemas import EasyMP3MetadataSongSchema
from .models import (
    LSearchModel,
    LSongModel,
    LAlbumModel,
    LArtistModel,
)
        for artist in self._artists.values():
            if artist.albums:
                artist.albums.sort(key=lambda x: (x.songs[0].date is None, x.songs[0].date), reverse=True)
            if artist.songs:
                artist.songs.sort(key=lambda x: x.title)


class LocalProvider(AbstractProvider):

    def __init__(self):
        super().__init__()

        self.library = Library()

    def scan(self, paths=None, depth=3):
        self.library.scan(paths, depth)
        self.library.sortout()

    @property
    def identifier(self):
        return 'local'

    @property
    def name(self):
        return '本地音乐'

    @property
    def songs(self):
        return self.library.list_songs()


# Path: fuocore/provider.py
# class AbstractProvider(ABC):
#     """abstract music resource provider
#     """
# 
#     # A well behaved provider should implement its own models .
#     Song = SongModel
#     Artist = ArtistModel
#     Album = AlbumModel
#     Playlist = PlaylistModel
#     Lyric = LyricModel
#     User = UserModel
# 
#     def __init__(self):
#         self._user = None
# 
# 
#     def get_model_cls(self, model_type):
#         name = _TYPE_NAME_MAP[model_type]
#         return getattr(self, name)
# 
#     def set_model_cls(self, model_type, model_cls):
#         name = _TYPE_NAME_MAP[model_type]
#         setattr(self, name, model_cls)
# 
#     @property
#     @abstractmethod
#     def identifier(self):
#         """provider identify"""
# 
#     @property
#     @abstractmethod
#     def name(self):
#         """provider name"""
# 
#     @contextmanager
#     def auth_as(self, user):
#         """auth as a user temporarily"""
#         old_user = self._user
#         self.auth(user)
#         try:
#             yield
#         finally:
#             self.auth(old_user)
# 
#     def auth(self, user):
#         """use provider as a specific user"""
#         self._user = user
# 
# Path: fuocore/utils.py
# def elfhash(s):
#     """
#     :param string: bytes
# 
#     >>> import base64
#     >>> s = base64.b64encode(b'hello world')
#     >>> elfhash(s)
#     224648685
#     """
#     hash = 0
#     x = 0
#     for c in s:
#         hash = (hash << 4) + c
#         x = hash & 0xF0000000
#         if x:
#             hash ^= (x >> 24)
#             hash &= ~x
#     return (hash & 0x7FFFFFFF)
# 
# Path: fuocore/utils.py
# def log_exectime(func):
#     @wraps(func)
#     def wrapper(*args, **kwargs):
#         t = time.process_time()
#         result = func(*args, **kwargs)
#         elapsed_time = time.process_time() - t
#         logger.debug('function %s executed time: %f ms',
#                      func.__name__, elapsed_time * 1000)
#         return result
#     return wrapper
# 
# Path: fuocore/local/schemas.py
# class EasyMP3MetadataSongSchema(Schema):
#     """EasyMP3 metadata"""
#     url = fields.Str(required=True)
#     duration = fields.Float(required=True)
#     title = fields.Str(required=True, missing=DEFAULT_TITLE)
#     artists_name = fields.Str(required=True, load_from='artist',
#                               missing=DEFAULT_ARTIST_NAME)
#     album_name = fields.Str(required=True, load_from='album',
#                             missing=DEFAULT_ALBUM_NAME)
#     album_artist_name = fields.Str(required=True, load_from='albumartist',
#                                    missing=DEFAULT_ARTIST_NAME)
#     track = fields.Str(load_from='tracknumber', missing='1/1')
#     disc = fields.Str(load_from='discnumber', missing='1/1')
#     date = fields.Str(missing='')
#     genre = fields.Str(missing='')
# 
#     # 本地歌曲目前都不支持描述和封面，将它们设置为空字符串
#     desc = fields.Str(missing='')
#     cover = fields.Str(missing='')
# 
# Path: fuocore/local/models.py
# class LSearchModel(SearchModel, LBaseModel):
#     pass
# 
# class LSongModel(SongModel, LBaseModel):
#     class Meta:
#         fields = ('disc', 'genre', 'date', 'track', 'cover', 'desc')
#         fields_no_get = ('lyric', )
# 
#     @classmethod
#     def get(cls, identifier):
#         return cls.meta.provider.library.get_song(identifier)
# 
#     @classmethod
#     def list(cls, identifier_list):
#         return map(cls.meta.provider.library._songs.get, identifier_list)
# 
# class LAlbumModel(AlbumModel, LBaseModel):
#     _detail_fields = ('songs',)
# 
#     @classmethod
#     def get(cls, identifier):
#         return cls.meta.provider.library.get_album(identifier)
# 
# class LArtistModel(ArtistModel, LBaseModel):
#     _detail_fields = ('songs',)
# 
#     @classmethod
#     def get(cls, identifier):
#         return cls.meta.provider.library.get_artist(identifier)


# Path: fuocore/local/provider.py
import base64
import logging
import os
import re
from fuzzywuzzy import process
from marshmallow.exceptions import ValidationError
from mutagen import MutagenError
from mutagen.mp3 import EasyMP3
from mutagen.easymp4 import EasyMP4
from fuocore.provider import AbstractProvider
from fuocore.utils import elfhash
from fuocore.utils import log_exectime
from .schemas import EasyMP3MetadataSongSchema
from .models import (
    LSearchModel,
    LSongModel,
    LAlbumModel,
    LArtistModel,
)

        for artist in self._artists.values():
            if artist.albums:
                artist.albums.sort(key=lambda x: (x.songs[0].date is None, x.songs[0].date), reverse=True)
            if artist.songs:
                artist.songs.sort(key=lambda x: x.title)


class LocalProvider(AbstractProvider):

    def __init__(self):
        super().__init__()

        self.library = Library()

    def scan(self, paths=None, depth=3):
        self.library.scan(paths, depth)
        self.library.sortout()

    @property
    def identifier(self):
        return 'local'

    @property
    def name(self):
        return '本地音乐'

    @property
    def songs(self):
        return self.library.list_songs()


    @log_exectime