Updated run_playlist to use function playlist engine
This commit is contained in:
parent
731913c8c8
commit
2b009eb6df
@ -1,25 +1,22 @@
|
||||
from google.cloud import firestore
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import random
|
||||
|
||||
from spotframework.engine.playlistengine import PlaylistEngine, PlaylistSource, RecommendationSource, LibraryTrackSource
|
||||
from spotframework.engine.processor.shuffle import Shuffle
|
||||
from spotframework.engine.processor.sort import SortReleaseDate
|
||||
from spotframework.engine.processor.deduplicate import DeduplicateByName
|
||||
import spotframework.util.monthstrings as monthstrings
|
||||
from spotframework.model.uri import Uri
|
||||
|
||||
from spotfm.engine.chart_source import ChartSource
|
||||
from spotframework.filter import remove_local
|
||||
from spotframework.filter.added import added_after
|
||||
from spotframework.filter.sort import sort_by_release_date
|
||||
from spotframework.filter.deduplicate import deduplicate_by_name
|
||||
|
||||
from fmframework.net.network import Network
|
||||
from spotfm.charts.chart import get_chart_of_spotify_tracks
|
||||
|
||||
import music.db.database as database
|
||||
from music.db.part_generator import PartGenerator
|
||||
from music.model.user import User
|
||||
from music.model.playlist import Playlist
|
||||
|
||||
db = firestore.Client()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -29,7 +26,7 @@ def run_user_playlist(username, playlist_name):
|
||||
|
||||
# PRE-RUN CHECKS
|
||||
if user is None:
|
||||
logger.error(f'user {username} not found')
|
||||
logger.error(f'user not found {username} / {playlist_name}')
|
||||
return
|
||||
|
||||
logger.info(f'running {username} / {playlist_name}')
|
||||
@ -37,11 +34,11 @@ def run_user_playlist(username, playlist_name):
|
||||
playlist = Playlist.collection.parent(user.key).filter('name', '==', playlist_name).get()
|
||||
|
||||
if playlist is None:
|
||||
logger.critical(f'playlist not found ({username}/{playlist_name})')
|
||||
logger.critical(f'playlist not found {username} / {playlist_name}')
|
||||
return
|
||||
|
||||
if playlist.uri is None:
|
||||
logger.critical(f'no playlist id to populate ({username}/{playlist_name})')
|
||||
logger.critical(f'no playlist id to populate {username} / {playlist_name}')
|
||||
return
|
||||
|
||||
# END CHECKS
|
||||
@ -49,71 +46,124 @@ def run_user_playlist(username, playlist_name):
|
||||
net = database.get_authed_spotify_network(user)
|
||||
|
||||
if net is None:
|
||||
logger.error(f'no spotify network returned for {username}')
|
||||
logger.error(f'no spotify network returned for {username} / {playlist_name}')
|
||||
return
|
||||
|
||||
engine = PlaylistEngine(net)
|
||||
user_playlists = net.get_user_playlists()
|
||||
|
||||
part_generator = PartGenerator(user=user)
|
||||
part_names = part_generator.get_recursive_parts(playlist.name)
|
||||
|
||||
spotify_playlist_names = part_generator.get_recursive_parts(playlist.name)
|
||||
playlist_tracks = []
|
||||
|
||||
processors = [DeduplicateByName()]
|
||||
params = [
|
||||
PlaylistSource.Params(names=spotify_playlist_names)
|
||||
]
|
||||
if playlist.add_last_month:
|
||||
part_names.append(monthstrings.get_last_month())
|
||||
if playlist.add_this_month:
|
||||
part_names.append(monthstrings.get_this_month())
|
||||
|
||||
# OPTIONS
|
||||
if playlist.include_recommendations:
|
||||
params.append(RecommendationSource.Params(recommendation_limit=playlist.recommendation_sample))
|
||||
# LOAD PLAYLIST TRACKS
|
||||
for part_name in part_names:
|
||||
try: # attempt to cast to uri
|
||||
uri = Uri(part_name)
|
||||
|
||||
if playlist.include_library_tracks:
|
||||
params.append(LibraryTrackSource.Params())
|
||||
# END OPTIONS
|
||||
|
||||
if playlist.type == 'fmchart':
|
||||
if user.lastfm_username is None:
|
||||
logger.error(f'{username} has no associated last.fm username, chart source skipped')
|
||||
_tracks = net.get_playlist_tracks(uri=uri)
|
||||
if _tracks and len(_tracks) > 0:
|
||||
playlist_tracks += _tracks
|
||||
else:
|
||||
logger.warning(f'no tracks returned for {uri} {username} / {playlist_name}')
|
||||
|
||||
except ValueError: # is a playlist name
|
||||
part_playlist = next((i for i in user_playlists if i.name == part_name), None)
|
||||
if part_playlist is None:
|
||||
logger.warning(f'playlist {part_name} not found {username} / {playlist_name}')
|
||||
continue
|
||||
|
||||
part_playlist_tracks = net.get_playlist_tracks(uri=part_playlist.uri)
|
||||
if part_playlist_tracks and len(part_playlist_tracks) > 0:
|
||||
playlist_tracks += part_playlist_tracks
|
||||
else:
|
||||
logger.warning(f'no tracks returned for {part_playlist.name} {username} / {playlist_name}')
|
||||
|
||||
playlist_tracks = remove_local(playlist_tracks)
|
||||
|
||||
# LIBRARY
|
||||
if playlist.include_library_tracks:
|
||||
library_tracks = net.get_library_tracks()
|
||||
if library_tracks and len(library_tracks) > 0:
|
||||
playlist_tracks += library_tracks
|
||||
else:
|
||||
logger.error(f'error getting library tracks {username} / {playlist_name}')
|
||||
|
||||
# PLAYLIST TYPE SPECIFIC
|
||||
if playlist.type == 'recents':
|
||||
boundary_date = datetime.datetime.now(datetime.timezone.utc) - \
|
||||
datetime.timedelta(days=int(playlist.day_boundary))
|
||||
playlist_tracks = added_after(playlist_tracks, boundary_date)
|
||||
elif playlist.type == 'fmchart':
|
||||
if user.lastfm_username is None:
|
||||
logger.error(f'no associated last.fm username, chart source skipped {username} / {playlist_name}')
|
||||
else:
|
||||
chart_range = Network.Range.MONTH
|
||||
try:
|
||||
chart_range = Network.Range[playlist.chart_range]
|
||||
except KeyError:
|
||||
logger.error(f'invalid last.fm chart range found for '
|
||||
f'{playlist_name}/{username} {playlist.chart_range}, defaulting to 1 month')
|
||||
logger.error(f'invalid last.fm chart range found {playlist.chart_range}, '
|
||||
f'defaulting to 1 month {username} / {playlist_name}')
|
||||
|
||||
engine.sources.append(ChartSource(spotnet=net, fmnet=database.get_authed_lastfm_network(user)))
|
||||
params.append(ChartSource.Params(chart_range=chart_range, limit=playlist.chart_limit))
|
||||
fmnet = database.get_authed_lastfm_network(user)
|
||||
if fmnet is not None:
|
||||
chart_tracks = get_chart_of_spotify_tracks(spotnet=net,
|
||||
fmnet=fmnet,
|
||||
period=chart_range,
|
||||
limit=playlist.chart_limit)
|
||||
|
||||
if chart_tracks is not None and len(chart_tracks) > 0:
|
||||
playlist_tracks += chart_tracks
|
||||
else:
|
||||
# INCLUDE SORT METHOD (no sorting for last.fm chart playlist)
|
||||
if playlist.shuffle is True:
|
||||
processors.append(Shuffle())
|
||||
logger.error(f'no tracks returned {username} / {playlist_name}')
|
||||
else:
|
||||
processors.append(SortReleaseDate(reverse=True))
|
||||
logger.error(f'no last.fm network returned {username} / {playlist_name}')
|
||||
|
||||
# GENERATE TRACKS
|
||||
if playlist.type == 'recents':
|
||||
boundary_date = datetime.datetime.now(datetime.timezone.utc) - \
|
||||
datetime.timedelta(days=int(playlist.day_boundary))
|
||||
tracks = engine.get_recent_playlist(params=params,
|
||||
processors=processors,
|
||||
boundary_date=boundary_date,
|
||||
add_this_month=playlist.add_this_month,
|
||||
add_last_month=playlist.add_last_month)
|
||||
# SORT METHOD
|
||||
if playlist.shuffle:
|
||||
random.shuffle(playlist_tracks)
|
||||
elif playlist.type != 'fmchart':
|
||||
playlist_tracks = sort_by_release_date(tracks=playlist_tracks, reverse=True)
|
||||
|
||||
# RECOMMENDATIONS
|
||||
if playlist.include_recommendations:
|
||||
recommendations = net.get_recommendations(tracks=[i.uri.object_id for i
|
||||
in random.sample(playlist_tracks, k=min(5, len(playlist_tracks)))
|
||||
if i.uri.object_type == Uri.ObjectType.track],
|
||||
response_limit=playlist.recommendation_sample)
|
||||
if recommendations and len(recommendations) > 0:
|
||||
playlist_tracks += recommendations
|
||||
else:
|
||||
tracks = engine.make_playlist(params=params,
|
||||
processors=processors)
|
||||
logger.error(f'error getting recommendations {username} / {playlist_name}')
|
||||
|
||||
# NET OPS
|
||||
engine.execute_playlist(tracks, Uri(playlist.uri))
|
||||
# DEDUPLICATE
|
||||
playlist_tracks = deduplicate_by_name(playlist_tracks)
|
||||
|
||||
overwrite = playlist.description_overwrite
|
||||
suffix = playlist.description_suffix
|
||||
# EXECUTE
|
||||
resp = net.replace_playlist_tracks(uri_string=playlist.uri, uris=[i.uri for i in playlist_tracks])
|
||||
if resp:
|
||||
if playlist.description_overwrite:
|
||||
string = playlist.description_overwrite
|
||||
else:
|
||||
string = ' / '.join(sorted(part_names))
|
||||
|
||||
if playlist.description_suffix:
|
||||
string += f' - {str(playlist.description_suffix)}'
|
||||
|
||||
if string is None or len(string) == 0:
|
||||
logger.error(f'no string generated {username} / {playlist_name}')
|
||||
return None
|
||||
|
||||
resp = net.change_playlist_details(Uri(playlist.uri), description=string)
|
||||
if resp is None:
|
||||
logger.error(f'error changing description {username} / {playlist_name}')
|
||||
else:
|
||||
logger.error(f'error executing {username} / {playlist_name}')
|
||||
|
||||
engine.change_description(sorted(spotify_playlist_names),
|
||||
uri=Uri(playlist.uri),
|
||||
overwrite=overwrite,
|
||||
suffix=suffix)
|
||||
playlist.last_updated = datetime.datetime.utcnow()
|
||||
playlist.update()
|
||||
|
2514
package-lock.json
generated
2514
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -19,24 +19,24 @@
|
||||
},
|
||||
"homepage": "https://github.com/Sarsoo/Music-Tools#readme",
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.9.5",
|
||||
"@material-ui/core": "^4.10.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"axios": "^0.19.2",
|
||||
"chart.js": "^2.9.3",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0",
|
||||
"react-router-dom": "^5.1.2"
|
||||
"react-router-dom": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"@babel/preset-react": "^7.9.4",
|
||||
"@babel/cli": "^7.10.1",
|
||||
"@babel/core": "^7.10.2",
|
||||
"@babel/preset-env": "^7.10.2",
|
||||
"@babel/preset-react": "^7.10.1",
|
||||
"babel-loader": "^8.1.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"css-loader": "^3.6.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-merge": "^4.2.2"
|
||||
}
|
||||
|
@ -1,37 +1,37 @@
|
||||
astroid==2.4.1
|
||||
astroid==2.4.2
|
||||
cachetools==4.1.0
|
||||
certifi==2020.4.5.1
|
||||
certifi==2020.4.5.2
|
||||
chardet==3.0.4
|
||||
click==7.1.2
|
||||
fireo==1.2.8
|
||||
fireo==1.3.3
|
||||
Flask==1.1.2
|
||||
google-api-core==1.17.0
|
||||
google-auth==1.14.3
|
||||
google-api-core==1.20.1
|
||||
google-auth==1.17.2
|
||||
google-cloud-core==1.3.0
|
||||
google-cloud-firestore==1.6.2
|
||||
google-cloud-firestore==1.7.0
|
||||
google-cloud-logging==1.15.0
|
||||
google-cloud-pubsub==1.5.0
|
||||
google-cloud-pubsub==1.6.0
|
||||
google-cloud-tasks==1.5.0
|
||||
googleapis-common-protos==1.51.0
|
||||
googleapis-common-protos==1.52.0
|
||||
grpc-google-iam-v1==0.12.3
|
||||
grpcio==1.29.0
|
||||
idna==2.9
|
||||
isort==4.3.21
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.2
|
||||
lazy-object-proxy==1.4.3
|
||||
lazy-object-proxy==1.5.0
|
||||
MarkupSafe==1.1.1
|
||||
mccabe==0.6.1
|
||||
numpy==1.18.4
|
||||
numpy==1.18.5
|
||||
opencv-python==4.2.0.34
|
||||
protobuf==3.11.3
|
||||
protobuf==3.12.2
|
||||
pyasn1==0.4.8
|
||||
pyasn1-modules==0.2.8
|
||||
pylint==2.5.2
|
||||
pylint==2.5.3
|
||||
pytz==2020.1
|
||||
requests==2.23.0
|
||||
rsa==4.0
|
||||
six==1.14.0
|
||||
rsa==4.6
|
||||
six==1.15.0
|
||||
tabulate==0.8.7
|
||||
toml==0.10.1
|
||||
urllib3==1.25.9
|
||||
|
@ -530,7 +530,6 @@ export class Edit extends Component{
|
||||
onChange={this.handleInputChange} />
|
||||
</Grid>
|
||||
}
|
||||
{ this.state.type == 'recents' &&
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
@ -547,7 +546,6 @@ export class Edit extends Component{
|
||||
labelPlacement="bottom"
|
||||
/>
|
||||
</Grid>
|
||||
}
|
||||
<Grid item xs={12}>
|
||||
<FormControl variant="filled">
|
||||
<InputLabel htmlFor="type-select">Type</InputLabel>
|
||||
|
@ -378,7 +378,7 @@ function StatsCard (props) {
|
||||
<Typography variant="h1" color="textPrimary" className={classes.root}>= { props.count }</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4" color="textSecondary" className={classes.root}>{ Math.round(props.proportion) }%</Typography>
|
||||
<Typography variant="h4" color="textSecondary" className={classes.root}>{ props.proportion.toFixed(2) }%</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
|
Loading…
Reference in New Issue
Block a user