adding docstrings, restructured reST
This commit is contained in:
parent
0a18597165
commit
7ade5ccaab
@ -6,6 +6,8 @@
|
|||||||
Set of utility tools for Spotify and Last.fm.
|
Set of utility tools for Spotify and Last.fm.
|
||||||
Built on my other libraries for Spotify ([spotframework](https://github.com/Sarsoo/spotframework)), Last.fm ([fmframework](https://github.com/Sarsoo/pyfmframework)) and interfacing utility tools for the two ([spotfm](https://github.com/Sarsoo/pyfmframework)). Currently running on a suite of Google Cloud Platform services. An iOS client is currently under development [here](https://github.com/Sarsoo/Music-Tools-iOS).
|
Built on my other libraries for Spotify ([spotframework](https://github.com/Sarsoo/spotframework)), Last.fm ([fmframework](https://github.com/Sarsoo/pyfmframework)) and interfacing utility tools for the two ([spotfm](https://github.com/Sarsoo/pyfmframework)). Currently running on a suite of Google Cloud Platform services. An iOS client is currently under development [here](https://github.com/Sarsoo/Music-Tools-iOS).
|
||||||
|
|
||||||
|
Read the full documentation [here](https://sarsoo.github.io/Music-Tools/).
|
||||||
|
|
||||||
# Smart Playlists
|
# Smart Playlists
|
||||||
|
|
||||||
Create smart playlists for Spotify including tracks from playlists, library and Spotify recommendations.
|
Create smart playlists for Spotify including tracks from playlists, library and Spotify recommendations.
|
||||||
|
@ -14,12 +14,16 @@ Music Tools
|
|||||||
src/music.model
|
src/music.model
|
||||||
src/music.tasks
|
src/music.tasks
|
||||||
|
|
||||||
Music Tools
|
`Music Tools <https://music.sarsoo.xyz>`_
|
||||||
-------------
|
----------------------------------------------
|
||||||
|
|
||||||
.. image:: https://github.com/sarsoo/music-tools/workflows/test%20and%20deploy/badge.svg
|
.. image:: https://github.com/sarsoo/music-tools/workflows/test%20and%20deploy/badge.svg
|
||||||
|
|
||||||
Music Tools is a web app for creating smart Spotify playlists.
|
Music Tools is a web app for creating smart Spotify playlists. The app is based on `spotframework <https://github.com/Sarsoo/spotframework>`_ and `fmframework <https://github.com/Sarsoo/pyfmframework>`_ for interfacing with Spotify and Last.fm. The app is currently hosted on Google's Cloud Platform.
|
||||||
|
|
||||||
|
The system is composed of a Flask web server with a Fireo ORM layer and longer tasks dispatched to Cloud Tasks or Functions.
|
||||||
|
|
||||||
|
.. image:: Playlists.png
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
|
@ -1,73 +1,6 @@
|
|||||||
music.api package
|
music.api
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
music.api.admin module
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.admin
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.api.api module
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.api
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.api.decorators module
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.decorators
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.api.fm module
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.fm
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.api.player module
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.player
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.api.spotfm module
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.spotfm
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.api.spotify module
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.spotify
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.api.tag module
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. automodule:: music.api.tag
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@ -75,3 +8,67 @@ Module contents
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.admin
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.admin
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.api
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.api
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.decorators
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.decorators
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.fm
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.fm
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.player
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.player
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.spotfm
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.spotfm
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.spotify
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.spotify
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
api.tag
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
.. automodule:: music.api.tag
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
@ -1,17 +1,6 @@
|
|||||||
music.auth package
|
music.auth
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
music.auth.auth module
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
.. automodule:: music.auth.auth
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@ -19,3 +8,11 @@ Module contents
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
auth.auth
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. automodule:: music.auth.auth
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
@ -1,25 +1,6 @@
|
|||||||
music.cloud package
|
music.cloud
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
music.cloud.function module
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.cloud.function
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.cloud.tasks module
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.cloud.tasks
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@ -27,3 +8,19 @@ Module contents
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
cloud.function
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.cloud.function
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
cloud.tasks
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.cloud.tasks
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
music.db package
|
music.db
|
||||||
================
|
================
|
||||||
|
|
||||||
Submodules
|
Module contents
|
||||||
----------
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: music.db
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
music.db.database module
|
music.db.database module
|
||||||
------------------------
|
------------------------
|
||||||
@ -19,11 +24,3 @@ music.db.part\_generator module
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
Module contents
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. automodule:: music.db
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
@ -1,41 +1,6 @@
|
|||||||
music.model package
|
music.model
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
music.model.config module
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.model.config
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.model.playlist module
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.model.playlist
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.model.tag module
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
.. automodule:: music.model.tag
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.model.user module
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. automodule:: music.model.user
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@ -43,3 +8,35 @@ Module contents
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
model.config
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.model.config
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
model.playlist
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.model.playlist
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
model.tag
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. automodule:: music.model.tag
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
model.user
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. automodule:: music.model.user
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
music package
|
music
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Subpackages
|
Subpackages
|
||||||
@ -14,8 +14,13 @@ Subpackages
|
|||||||
music.model
|
music.model
|
||||||
music.tasks
|
music.tasks
|
||||||
|
|
||||||
Submodules
|
Module contents
|
||||||
----------
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: music
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
music.music module
|
music.music module
|
||||||
------------------
|
------------------
|
||||||
@ -24,11 +29,3 @@ music.music module
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
Module contents
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. automodule:: music
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
@ -1,41 +1,6 @@
|
|||||||
music.tasks package
|
music.tasks
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
music.tasks.create\_playlist module
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.tasks.create_playlist
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.tasks.refresh\_lastfm\_stats module
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.tasks.refresh_lastfm_stats
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.tasks.run\_user\_playlist module
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.tasks.run_user_playlist
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
music.tasks.update\_tag module
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
.. automodule:: music.tasks.update_tag
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@ -43,3 +8,35 @@ Module contents
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
tasks.create\_playlist
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.tasks.create_playlist
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
tasks.refresh\_lastfm\_stats
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.tasks.refresh_lastfm_stats
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
tasks.run\_user\_playlist
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.tasks.run_user_playlist
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
tasks.update\_tag
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. automodule:: music.tasks.update_tag
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
@ -5,8 +5,14 @@ publisher = pubsub_v1.PublisherClient()
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def update_tag(username, tag_id):
|
def update_tag(username: str, tag_id: str) -> None:
|
||||||
"""Queue serverless tag update for user"""
|
"""Queue serverless tag update for user
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (str): Subject username
|
||||||
|
tag_id (str): Subject tag ID
|
||||||
|
"""
|
||||||
|
|
||||||
logger.info(f'queuing {tag_id} update for {username}')
|
logger.info(f'queuing {tag_id} update for {username}')
|
||||||
|
|
||||||
if username is None or tag_id is None:
|
if username is None or tag_id is None:
|
||||||
@ -20,8 +26,14 @@ def update_tag(username, tag_id):
|
|||||||
publisher.publish('projects/sarsooxyz/topics/update_tag', b'', tag_id=tag_id, username=username)
|
publisher.publish('projects/sarsooxyz/topics/update_tag', b'', tag_id=tag_id, username=username)
|
||||||
|
|
||||||
|
|
||||||
def run_user_playlist_function(username, playlist_name):
|
def run_user_playlist_function(username: str, playlist_name: str) -> None:
|
||||||
"""Queue serverless playlist update for user"""
|
"""Queue serverless playlist update for user
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (str): Subject username
|
||||||
|
playlist_name (str): Subject tag ID
|
||||||
|
"""
|
||||||
|
|
||||||
logger.info(f'queuing {playlist_name} update for {username}')
|
logger.info(f'queuing {playlist_name} update for {username}')
|
||||||
|
|
||||||
if username is None or playlist_name is None:
|
if username is None or playlist_name is None:
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""Functions for creating GCP Cloud Tasks for long running operatings
|
||||||
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@ -48,8 +51,12 @@ def update_all_user_playlists():
|
|||||||
seconds_delay += 30
|
seconds_delay += 30
|
||||||
|
|
||||||
|
|
||||||
def update_playlists(username):
|
def update_playlists(username: str):
|
||||||
"""Refresh all playlists for given user, environment dependent"""
|
"""Refresh all playlists for given user, environment dependent
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (str): Subject user's username
|
||||||
|
"""
|
||||||
|
|
||||||
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
|
|
||||||
@ -73,8 +80,14 @@ def update_playlists(username):
|
|||||||
seconds_delay += 6
|
seconds_delay += 6
|
||||||
|
|
||||||
|
|
||||||
def run_user_playlist_task(username, playlist_name, delay=0):
|
def run_user_playlist_task(username: str, playlist_name: str, delay: int = 0):
|
||||||
"""Create tasks for a users given playlist"""
|
"""Create tasks for a users given playlist
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (str): Subject user's username
|
||||||
|
playlist_name (str): Subject playlist name
|
||||||
|
delay (int, optional): Seconds to delay execution by. Defaults to 0.
|
||||||
|
"""
|
||||||
|
|
||||||
task = {
|
task = {
|
||||||
'app_engine_http_request': { # Specify the type of request.
|
'app_engine_http_request': { # Specify the type of request.
|
||||||
@ -123,8 +136,12 @@ def refresh_all_user_playlist_stats():
|
|||||||
logger.debug(f'skipping {iter_user.username}')
|
logger.debug(f'skipping {iter_user.username}')
|
||||||
|
|
||||||
|
|
||||||
def refresh_user_playlist_stats(username):
|
def refresh_user_playlist_stats(username: str):
|
||||||
"""Refresh all playlist stats for given user, environment dependent"""
|
"""Refresh all playlist stats for given user, environment dependent
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (str): Subject user's username
|
||||||
|
"""
|
||||||
|
|
||||||
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
user = User.collection.filter('username', '==', username.strip().lower()).get()
|
||||||
if user is None:
|
if user is None:
|
||||||
@ -150,8 +167,13 @@ def refresh_user_playlist_stats(username):
|
|||||||
logger.error('no last.fm username')
|
logger.error('no last.fm username')
|
||||||
|
|
||||||
|
|
||||||
def refresh_user_stats_task(username, delay=0):
|
def refresh_user_stats_task(username: str, delay: int = 0):
|
||||||
"""Create user playlist stats refresh task"""
|
"""Create user playlist stats refresh task
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (str): Subject user's username
|
||||||
|
delay (int, optional): Seconds to delay execution by. Defaults to 0.
|
||||||
|
"""
|
||||||
|
|
||||||
task = {
|
task = {
|
||||||
'app_engine_http_request': { # Specify the type of request.
|
'app_engine_http_request': { # Specify the type of request.
|
||||||
@ -172,8 +194,14 @@ def refresh_user_stats_task(username, delay=0):
|
|||||||
tasker.create_task(task_path, task)
|
tasker.create_task(task_path, task)
|
||||||
|
|
||||||
|
|
||||||
def refresh_playlist_task(username, playlist_name, delay=0):
|
def refresh_playlist_task(username: str, playlist_name: str, delay: int = 0):
|
||||||
"""Create user playlist stats refresh tasks"""
|
"""Create user playlist stats refresh tasks
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (str): Subject user's username
|
||||||
|
playlist_name (str): Subject playlist name
|
||||||
|
delay (int, optional): Seconds to delay execution by. Defaults to 0.
|
||||||
|
"""
|
||||||
|
|
||||||
track_task = {
|
track_task = {
|
||||||
'app_engine_http_request': { # Specify the type of request.
|
'app_engine_http_request': { # Specify the type of request.
|
||||||
@ -230,7 +258,7 @@ def refresh_playlist_task(username, playlist_name, delay=0):
|
|||||||
|
|
||||||
|
|
||||||
def update_all_user_tags():
|
def update_all_user_tags():
|
||||||
"""Create user tag refresh task sfor all users"""
|
"""Create user tag refresh task for all users"""
|
||||||
|
|
||||||
seconds_delay = 0
|
seconds_delay = 0
|
||||||
logger.info('running')
|
logger.info('running')
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta, datetime, timezone
|
from datetime import timedelta, datetime, timezone
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from spotframework.net.network import Network as SpotifyNetwork, SpotifyNetworkException
|
from spotframework.net.network import Network as SpotifyNetwork, SpotifyNetworkException
|
||||||
from spotframework.net.user import NetworkUser
|
from spotframework.net.user import NetworkUser
|
||||||
@ -11,7 +12,15 @@ from music.model.config import Config
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def refresh_token_database_callback(user):
|
def refresh_token_database_callback(user: User) -> None:
|
||||||
|
"""Callback for handling when a spotframework network updates user credemtials
|
||||||
|
|
||||||
|
Used to store newly authenticated credentials
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): Subject user
|
||||||
|
"""
|
||||||
|
|
||||||
if isinstance(user, DatabaseUser):
|
if isinstance(user, DatabaseUser):
|
||||||
user_obj = User.collection.filter('username', '==', user.user_id.strip().lower()).get()
|
user_obj = User.collection.filter('username', '==', user.user_id.strip().lower()).get()
|
||||||
if user_obj is None:
|
if user_obj is None:
|
||||||
@ -29,7 +38,16 @@ def refresh_token_database_callback(user):
|
|||||||
logger.error('user has no attached id')
|
logger.error('user has no attached id')
|
||||||
|
|
||||||
|
|
||||||
def get_authed_spotify_network(user):
|
def get_authed_spotify_network(user: User) -> Optional[SpotifyNetwork]:
|
||||||
|
"""Get an authenticated spotframework network for a given user
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): Subject user to retrieve a network for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[SpotifyNetwork]: Authenticated spotframework network
|
||||||
|
"""
|
||||||
|
|
||||||
if user is not None:
|
if user is not None:
|
||||||
if user.spotify_linked:
|
if user.spotify_linked:
|
||||||
config = Config.collection.get("config/music-tools")
|
config = Config.collection.get("config/music-tools")
|
||||||
@ -62,7 +80,16 @@ def get_authed_spotify_network(user):
|
|||||||
logger.error(f'no user provided')
|
logger.error(f'no user provided')
|
||||||
|
|
||||||
|
|
||||||
def get_authed_lastfm_network(user):
|
def get_authed_lastfm_network(user: User) -> Optional[FmNetwork]:
|
||||||
|
"""Get an authenticated fmframework network for a given user
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): Subject user to retrieve a network for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[FmNetwork]: Authenticated fmframework network
|
||||||
|
"""
|
||||||
|
|
||||||
if user is not None:
|
if user is not None:
|
||||||
if user.lastfm_username:
|
if user.lastfm_username:
|
||||||
config = Config.collection.get("config/music-tools")
|
config = Config.collection.get("config/music-tools")
|
||||||
@ -75,5 +102,5 @@ def get_authed_lastfm_network(user):
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DatabaseUser(NetworkUser):
|
class DatabaseUser(NetworkUser):
|
||||||
"""adding music tools username to spotframework network user"""
|
"""Adding Music Tools username to spotframework network user"""
|
||||||
user_id: str = None
|
user_id: str = None
|
||||||
|
@ -1,13 +1,27 @@
|
|||||||
from music.model.user import User
|
from music.model.user import User
|
||||||
from music.model.playlist import Playlist
|
from music.model.playlist import Playlist
|
||||||
import logging
|
import logging
|
||||||
|
from typing import List
|
||||||
|
from google.cloud.firestore import DocumentReference
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PartGenerator:
|
class PartGenerator:
|
||||||
|
"""Resolve a playlists components from other referenced smart playlists
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, user: User = None, username: str = None):
|
def __init__(self, user: User = None, username: str = None):
|
||||||
|
"""Initialise with user to resolve for
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User, optional): Subject user. Defaults to None.
|
||||||
|
username (str, optional): Subject username. Defaults to None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
LookupError: No user returned when querying for username
|
||||||
|
NameError: No user provided
|
||||||
|
"""
|
||||||
self.queried_playlists = []
|
self.queried_playlists = []
|
||||||
self.parts = []
|
self.parts = []
|
||||||
|
|
||||||
@ -23,18 +37,35 @@ class PartGenerator:
|
|||||||
raise NameError('no user info provided')
|
raise NameError('no user info provided')
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
"""Reset internal state for resolved playlists
|
||||||
|
"""
|
||||||
|
|
||||||
self.queried_playlists = []
|
self.queried_playlists = []
|
||||||
self.parts = []
|
self.parts = []
|
||||||
|
|
||||||
def get_recursive_parts(self, name):
|
def get_recursive_parts(self, name: str) -> List[str]:
|
||||||
|
"""Resolve and return a playlist's component Spotify playlist names
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): Subject smart playlist name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[str]: Resolved list of component playlists
|
||||||
|
"""
|
||||||
|
|
||||||
logger.info(f'getting part from {name} for {self.user.username}')
|
logger.info(f'getting part from {name} for {self.user.username}')
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
self.process_reference_by_name(name)
|
self.process_reference_by_name(name)
|
||||||
|
|
||||||
return [i for i in {i for i in self.parts}]
|
return list({i for i in self.parts})
|
||||||
|
|
||||||
def process_reference_by_name(self, name):
|
def process_reference_by_name(self, name: str) -> None:
|
||||||
|
"""Resolve a smart playlist by name, recurses into process_reference_by_reference
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): Subject playlist name
|
||||||
|
"""
|
||||||
|
|
||||||
playlist = Playlist.collection.parent(self.user.key).filter('name', '==', name).get()
|
playlist = Playlist.collection.parent(self.user.key).filter('name', '==', name).get()
|
||||||
|
|
||||||
@ -55,7 +86,12 @@ class PartGenerator:
|
|||||||
else:
|
else:
|
||||||
logger.warning(f'playlist reference {name} not found')
|
logger.warning(f'playlist reference {name} not found')
|
||||||
|
|
||||||
def process_reference_by_reference(self, ref):
|
def process_reference_by_reference(self, ref: DocumentReference):
|
||||||
|
"""Recursive resolution function for walking a playlist's dependencies by DocumentReference
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ref (DocumentReference): Subject Firestore document for resolving
|
||||||
|
"""
|
||||||
|
|
||||||
if ref.id not in self.queried_playlists:
|
if ref.id not in self.queried_playlists:
|
||||||
playlist_reference_object = ref.get().to_dict()
|
playlist_reference_object = ref.get().to_dict()
|
||||||
|
@ -3,12 +3,19 @@ from fireo.fields import TextField, BooleanField, DateTime, NumberField, ListFie
|
|||||||
|
|
||||||
|
|
||||||
class Config(Model):
|
class Config(Model):
|
||||||
|
"""Service-level config data structure for app keys and settings
|
||||||
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
collection_name = 'config'
|
collection_name = 'config'
|
||||||
|
"""Set correct path in Firestore
|
||||||
|
"""
|
||||||
|
|
||||||
spotify_client_id = TextField()
|
spotify_client_id = TextField()
|
||||||
spotify_client_secret = TextField()
|
spotify_client_secret = TextField()
|
||||||
last_fm_client_id = TextField()
|
last_fm_client_id = TextField()
|
||||||
|
|
||||||
playlist_cloud_operating_mode = TextField() # task, function
|
playlist_cloud_operating_mode = TextField() # task, function
|
||||||
|
"""Determines whether playlist and tag update operations are done by Cloud Tasks or Functions
|
||||||
|
"""
|
||||||
secret_key = TextField()
|
secret_key = TextField()
|
||||||
|
@ -1,12 +1,27 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import music.db.database as database
|
import music.db.database as database
|
||||||
from spotframework.net.network import SpotifyNetworkException
|
from spotframework.net.network import SpotifyNetworkException
|
||||||
|
from spotframework.model.playlist import FullPlaylist
|
||||||
|
|
||||||
|
from music.model.user import User
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def create_playlist(user, name):
|
def create_playlist(user: User, name: str) -> Optional[FullPlaylist]:
|
||||||
|
"""Create a new playlist on the user's Spotify account
|
||||||
|
|
||||||
|
For creating new playlists, create and return a new playlist object
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): Subject user
|
||||||
|
name (str): Name of new playlist
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[FullPlaylist]: New playlist object if created
|
||||||
|
"""
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
logger.error(f'username not provided')
|
logger.error(f'username not provided')
|
||||||
|
@ -10,6 +10,7 @@ from spotframework.filter.sort import sort_by_release_date
|
|||||||
from spotframework.filter.deduplicate import deduplicate_by_name
|
from spotframework.filter.deduplicate import deduplicate_by_name
|
||||||
from spotframework.net.network import SpotifyNetworkException
|
from spotframework.net.network import SpotifyNetworkException
|
||||||
|
|
||||||
|
from spotframework.net.network import Network as SpotNetwork
|
||||||
from fmframework.net.network import Network
|
from fmframework.net.network import Network
|
||||||
from spotfm.chart import map_lastfm_track_chart_to_spotify
|
from spotfm.chart import map_lastfm_track_chart_to_spotify
|
||||||
|
|
||||||
@ -21,8 +22,25 @@ from music.model.playlist import Playlist
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def run_user_playlist(user, playlist, spotnet=None, fmnet=None):
|
def run_user_playlist(user: User, playlist: Playlist, spotnet: SpotNetwork = None, fmnet: Network = None) -> None:
|
||||||
"""Generate and upadate a user's playlist"""
|
"""Generate and upadate a user's smart playlist
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): Subject user
|
||||||
|
playlist (Playlist): User's subject playlist
|
||||||
|
spotnet (SpotNetwork, optional): Spotframework network for Spotify operations. Defaults to None.
|
||||||
|
fmnet (Network, optional): Fmframework network for Last.fm operations. Defaults to None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NameError: No user provided
|
||||||
|
NameError: No playlist provided
|
||||||
|
AttributeError: Playlist has no URI
|
||||||
|
NameError: No spotframework network available
|
||||||
|
e: spotframework error when retrieving user playlists
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
[type]: [description]
|
||||||
|
"""
|
||||||
|
|
||||||
# PRE-RUN CHECKS
|
# PRE-RUN CHECKS
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user