Skip to content
This repository has been archived by the owner on Jun 30, 2019. It is now read-only.

Commit

Permalink
bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
cosven committed Dec 29, 2018
1 parent 4f19150 commit 94fc6e4
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 42 deletions.
7 changes: 4 additions & 3 deletions fuocore/local/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ class Meta:

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._songs.get(identifier)
return cls.meta.provider.library.get_song(identifier)

@classmethod
def list(cls, identifier_list):
Expand All @@ -39,15 +40,15 @@ class LAlbumModel(AlbumModel, LBaseModel):

@classmethod
def get(cls, identifier):
return cls.meta.provider.library._albums.get(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._artists.get(identifier)
return cls.meta.provider.library.get_artist(identifier)


class LSearchModel(SearchModel, LBaseModel):
Expand Down
66 changes: 39 additions & 27 deletions fuocore/local/provider.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-

# pylint: disable=wrong-import-position
"""
TODO: 这个模块中目前逻辑非常多,包括音乐目录扫描、音乐库的构建等小部分,
这些小部分理论都可以从中拆除。
"""

import base64
import logging
import pickle
import os
import re

Expand Down Expand Up @@ -57,12 +56,12 @@ def create_artist(identifier, name):


def create_album(identifier, name):
return LArtistModel(identifier=identifier,
name=name,
songs=[],
artists=[],
desc='',
cover='',)
return LAlbumModel(identifier=identifier,
name=name,
songs=[],
artists=[],
desc='',
cover='',)


def add_song(fpath, g_songs, g_artists, g_albums):
Expand Down Expand Up @@ -107,11 +106,12 @@ def add_song(fpath, g_songs, g_artists, g_albums):
album_artist_name = data['album_artist_name']

# 生成 song model
song_id_str = ' - '.join([title, artists_name, album_name, duration])
song_id_str = ' - '.join([title, artists_name, album_name, str(int(duration))])
song_id = gen_id(song_id_str)
if song_id in g_songs:
# 剩下 album, artists, lyric 三个字段没有初始化
if song_id not in g_songs:
# 剩下 album, lyric 三个字段没有初始化
song = LSongModel(identifier=song_id,
artists=[],
title=title,
url=fpath,
duration=duration,
Expand All @@ -123,9 +123,10 @@ def add_song(fpath, g_songs, g_artists, g_albums):
desc=data['desc'],
disc=data['disc'],
track=data['track'])
g_songs[song_id] = song
else:
song = g_songs[song_id]
logger.warning('duplicate song: {} {}'.format(song.url, fpath))
logger.warning('duplicate song: %s %s', song.url, fpath)
return

# 生成 album artist model
Expand All @@ -141,24 +142,27 @@ def add_song(fpath, g_songs, g_artists, g_albums):
album_id = gen_id(album_id_str)
if album_id not in g_albums:
album = create_album(album_id, album_name)
g_albums[album_id] = album
else:
album = g_albums[album_id]

# 处理专辑的歌手信息,专辑歌手的专辑列表信息
# 处理专辑的歌手信息和歌曲信息,专辑歌手的专辑列表信息
if album not in album_artist.albums:
album_artist.albums.append(album)
if album_artist not in album.artists:
album.artists.append(album_artist)
if song not in album.songs:
album.songs.append(song)

# 处理歌曲的歌手和专辑信息,以及歌手的歌曲列表
song.artists.append(album_artist)
song.album = album
for artist_name in artist_name_list:
artist_id = gen_id(artist_name)
if artist_id in g_artists:
artist = g_artists[artist_id]
else:
artist = create_artist(identifier=artist_id, name=artist_name)
g_artists[artist_id] = artist
if artist not in song.artists:
song.artists.append(artist)
if song not in artist.songs:
Expand All @@ -168,39 +172,43 @@ def add_song(fpath, g_songs, g_artists, g_albums):
class Library:
DEFAULT_MUSIC_FOLDER = os.path.expanduser('~') + '/Music'

def __init__(self, paths=None, depth=2):
def __init__(self):
self._songs = {}
self._albums = {}
self._artists = {}

self.depth = depth
self.paths = paths or [Library.DEFAULT_MUSIC_FOLDER]

def list_songs(self):
return list(self._songs.values())

# TODO:
def get_song(self, identifier):
return self._songs.get(identifier)

def get_album(self, identifier):
return self._albums.get(identifier)

def get_artist(self, identifier):
return self._artists.get(identifier)

@log_exectime
def scan(self):
def scan(self, paths=None, depth=2):
"""scan media files in all paths
"""
song_exts = ['mp3', 'ogg', 'wma', 'm4a']
exts = song_exts
depth = self.depth if self.depth <= 3 else 3
paths = paths or [Library.DEFAULT_MUSIC_FOLDER]
depth = depth if depth <= 3 else 3
media_files = []
for directory in self.paths:
for directory in paths:
logger.debug('正在扫描目录(%s)...', directory)
media_files.extend(scan_directory(directory, exts, depth))
logger.info('共扫描到 %d 个音乐文件,准备将其录入本地音乐库', len(media_files))

for fpath in media_files:
add_song(fpath, self._songs, self._artists, self._albums)
logger.debug('扫描到 %d 首歌曲', len(self._songs))
logger.info('录入本地音乐库完毕')

def sortout(self):
for album in self.albums.values():
for album in self._albums.values():
try:
album.songs.sort(key=lambda x: (int(x.disc.split('/')[0]), int(x.track.split('/')[0])))
except Exception as e:
Expand All @@ -221,7 +229,7 @@ def __init__(self):
self.library = Library()

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

@property
Expand All @@ -234,7 +242,6 @@ def name(self):

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

@log_exectime
Expand All @@ -257,4 +264,9 @@ def search(self, keyword, **kwargs):
provider = LocalProvider()

from .schemas import EasyMP3MetadataSongSchema
from .models import LSearchModel, LSongModel, LAlbumModel, LArtistModel
from .models import (
LSearchModel,
LSongModel,
LAlbumModel,
LArtistModel,
)
19 changes: 13 additions & 6 deletions fuocore/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ class ModelType(IntEnum):
user = 17


class ModelStage(IntEnum):
"""Model 所处的阶段,有大小关系"""
display = 4
inited = 8
gotten = 16


class ModelMetadata(object):
def __init__(self,
model_type=ModelType.dummy.value,
Expand Down Expand Up @@ -78,7 +85,7 @@ def __init__(self, name):
self.value_display = ""

def __get__(self, instance, _=None):
if instance.gotten:
if instance.stage >= ModelStage.inited:
return getattr(instance, self.name_real)
return self.value_display

Expand Down Expand Up @@ -194,8 +201,7 @@ class Meta:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

#: 是否已经调用过 gotten,通常也意味着字段是否都已经初始化
self.gotten = kwargs.get('gotten', True)
self.stage = kwargs.get('stage', ModelStage.inited)

def __eq__(self, other):
if not isinstance(other, BaseModel):
Expand All @@ -216,7 +222,8 @@ def __getattribute__(self, name):
value = object.__getattribute__(self, name)
if name in cls.meta.fields \
and name not in cls.meta.fields_no_get \
and value is None:
and value is None \
and self.stage < ModelStage.gotten:
if cls.meta.allow_get:
logger.info("Model {} {}'s value is None, try to get detail."
.format(repr(self), name))
Expand All @@ -228,7 +235,7 @@ def __getattribute__(self, name):
# 这里不能使用 getattr,否则有可能会无限 get
fv = object.__getattribute__(obj, field)
setattr(self, field, fv)
self.gotten = True
self.stage = ModelStage.gotten
else:
logger.warning('Model {} get return None'.format(cls_name))
else:
Expand All @@ -239,7 +246,7 @@ def __getattribute__(self, name):
@classmethod
def create_by_display(cls, identifier, **kwargs):
model = cls(identifier=identifier)
model.gotten = False
model.stage = ModelStage.display
for k, v in kwargs.items():
if k in cls.meta.fields_display:
setattr(model, k + '_display', v)
Expand Down
6 changes: 1 addition & 5 deletions fuocore/netease/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,13 @@ def desc(self, value):


class NPlaylistModel(PlaylistModel, NBaseModel):

class Meta:
fields = ('uid')

@classmethod
def get(cls, identifier):
data = cls._api.playlist_detail(identifier)
playlist, _ = NeteasePlaylistSchema(strict=True).load(data)

# 当歌单的描述是空时,desc 的值为 None,这里手动设置为空
if playlist.desc is None:
playlist.desc = ''
return playlist

def add(self, song_id, allow_exist=True):
Expand Down Expand Up @@ -196,6 +191,7 @@ class NSearchModel(SearchModel, NBaseModel):
class NUserModel(UserModel, NBaseModel):
class Meta:
fields = ('cookies', )
fields_no_get = ('cookies', )

@classmethod
def get(cls, identifier):
Expand Down
2 changes: 1 addition & 1 deletion fuocore/netease/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def create_model(self, data):
song.album.cover = None
song.album.songs = None
if data.get('desc') is None:
data.pop('desc')
data['desc'] = ''
return NPlaylistModel(**data)


Expand Down

0 comments on commit 94fc6e4

Please sign in to comment.