diff --git a/music/api/tag.py b/music/api/tag.py
index c7aa9cf..08d7b16 100644
--- a/music/api/tag.py
+++ b/music/api/tag.py
@@ -38,7 +38,7 @@ def tag_route(tag_id, user=None):
def get_tag(tag_id, user):
- logger.info(f'retriving {tag_id} for {user.username}')
+ logger.info(f'retrieving {tag_id} for {user.username}')
db_tag = Tag.collection.parent(user.key).filter('tag_id', '==', tag_id).get()
if db_tag is not None:
@@ -62,6 +62,9 @@ def put_tag(tag_id, user):
if request_json.get('name'):
db_tag.name = request_json['name'].strip()
+ if request_json.get('time_objects') is not None:
+ db_tag.time_objects = request_json['time_objects']
+
if request_json.get('tracks') is not None:
db_tag.tracks = [
{
diff --git a/music/model/tag.py b/music/model/tag.py
index bb9faa7..9182ed3 100644
--- a/music/model/tag.py
+++ b/music/model/tag.py
@@ -1,5 +1,5 @@
from fireo.models import Model
-from fireo.fields import TextField, DateTime, NumberField, ListField
+from fireo.fields import TextField, DateTime, NumberField, ListField, BooleanField
class Tag(Model):
@@ -20,6 +20,10 @@ class Tag(Model):
last_updated = DateTime()
+ time_objects = BooleanField(default=False)
+ total_time = TextField(default='00:00:00')
+ total_time_ms = NumberField(default=0)
+
def to_dict(self):
to_return = super().to_dict()
diff --git a/music/tasks/update_tag.py b/music/tasks/update_tag.py
index c605ce1..45701d0 100644
--- a/music/tasks/update_tag.py
+++ b/music/tasks/update_tag.py
@@ -7,6 +7,8 @@ from music.model.tag import Tag
from fmframework.net.network import LastFMNetworkException
+from spotfm.timer import time, seconds_to_time_str
+
logger = logging.getLogger(__name__)
@@ -29,26 +31,48 @@ def update_tag(username, tag_id):
return
net = database.get_authed_lastfm_network(user)
-
if net is None:
- logger.error(f'no last.fm network returned for {username}')
+ logger.error(f'no last.fm network returned for {username} / {tag_id}')
return
- tag_count = 0
+ if tag.time_objects:
+ if user.spotify_linked:
+ spotnet = database.get_authed_spotify_network(user)
+ else:
+ logger.warning(f'timing objects requested but no spotify linked {username} / {tag_id}')
+
+ tag.count = 0
+ tag.total_time_ms = 0
+
try:
user_scrobbles = net.user_scrobble_count()
except LastFMNetworkException:
logger.exception(f'error retrieving scrobble count {username} / {tag_id}')
- user_scrobbles = 0
+ user_scrobbles = 1
artists = []
for artist in tag.artists:
try:
- net_artist = net.artist(name=artist['name'])
+ if tag.time_objects and user.spotify_linked:
+ total_ms, timed_tracks = time(spotnet=spotnet, fmnet=net,
+ artist=artist['name'], username=user.lastfm_username,
+ return_tracks=True)
+ scrobbles = sum(i[0].user_scrobbles for i in timed_tracks)
- if net_artist is not None:
- artist['count'] = net_artist.user_scrobbles
- tag_count += net_artist.user_scrobbles
+ artist['time_ms'] = total_ms
+ artist['time'] = seconds_to_time_str(milliseconds=total_ms)
+ tag.total_time_ms += total_ms
+
+ else:
+ net_artist = net.artist(name=artist['name'])
+
+ if net_artist is not None:
+ scrobbles = net_artist.user_scrobbles
+ else:
+ scrobbles = 0
+
+ artist['count'] = scrobbles
+ tag.count += scrobbles
except LastFMNetworkException:
logger.exception(f'error during artist retrieval {username} / {tag_id}')
@@ -57,13 +81,28 @@ def update_tag(username, tag_id):
albums = []
for album in tag.albums:
try:
- net_album = net.album(name=album['name'], artist=album['artist'])
+ if tag.time_objects and user.spotify_linked:
+ total_ms, timed_tracks = time(spotnet=spotnet, fmnet=net,
+ album=album['name'], artist=album['artist'],
+ username=user.lastfm_username, return_tracks=True)
+ scrobbles = sum(i[0].user_scrobbles for i in timed_tracks)
- if net_album is not None:
- album['count'] = net_album.user_scrobbles
+ album['time_ms'] = total_ms
+ album['time'] = seconds_to_time_str(milliseconds=total_ms)
+ tag.total_time_ms += total_ms
- if album['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]:
- tag_count += net_album.user_scrobbles
+ else:
+ net_album = net.album(name=album['name'], artist=album['artist'])
+
+ if net_album is not None:
+ scrobbles = net_album.user_scrobbles
+ else:
+ scrobbles = 0
+
+ album['count'] = scrobbles
+
+ if album['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]:
+ tag.count += scrobbles
except LastFMNetworkException:
logger.exception(f'error during album retrieval {username} / {tag_id}')
@@ -72,13 +111,28 @@ def update_tag(username, tag_id):
tracks = []
for track in tag.tracks:
try:
- net_track = net.track(name=track['name'], artist=track['artist'])
+ if tag.time_objects and user.spotify_linked:
+ total_ms, timed_tracks = time(spotnet=spotnet, fmnet=net,
+ track=track['name'], artist=track['artist'],
+ username=user.lastfm_username, return_tracks=True)
+ scrobbles = sum(i[0].user_scrobbles for i in timed_tracks)
- if net_track is not None:
- track['count'] = net_track.user_scrobbles
+ track['time_ms'] = total_ms
+ track['time'] = seconds_to_time_str(milliseconds=total_ms)
+ tag.total_time_ms += total_ms
- if track['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]:
- tag_count += net_track.user_scrobbles
+ else:
+ net_track = net.track(name=track['name'], artist=track['artist'])
+
+ if net_track is not None:
+ scrobbles = net_track.user_scrobbles
+ else:
+ scrobbles = 0
+
+ track['count'] = scrobbles
+
+ if track['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]:
+ tag.count += scrobbles
except LastFMNetworkException:
logger.exception(f'error during track retrieval {username} / {tag_id}')
@@ -88,9 +142,9 @@ def update_tag(username, tag_id):
tag.albums = albums
tag.artists = artists
+ tag.total_time = seconds_to_time_str(milliseconds=tag.total_time_ms)
tag.total_user_scrobbles = user_scrobbles
- tag.count = tag_count
- tag.proportion = (tag_count / user_scrobbles) * 100
+ tag.proportion = (tag.count / user_scrobbles) * 100
tag.last_updated = datetime.utcnow()
tag.update()
diff --git a/requirements.txt b/requirements.txt
index 8197f51..285e817 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,10 @@
astroid==2.4.2
+beautifulsoup4==4.9.1
cachetools==4.1.0
certifi==2020.6.20
chardet==3.0.4
click==7.1.2
+colorama==0.4.3
fireo==1.3.3
Flask==1.1.2
google-api-core==1.21.0
@@ -32,6 +34,7 @@ pytz==2020.1
requests==2.24.0
rsa==4.6
six==1.15.0
+soupsieve==2.0.1
tabulate==0.8.7
toml==0.10.1
urllib3==1.25.9
diff --git a/src/js/Tag/View.js b/src/js/Tag/View.js
index 92cfcf0..5e52a7d 100644
--- a/src/js/Tag/View.js
+++ b/src/js/Tag/View.js
@@ -1,7 +1,7 @@
import React, { Component } from "react";
const axios = require('axios');
-import { Card, Button, CircularProgress, CardActions, CardContent, FormControl, InputLabel, Select, Typography, Grid, TextField, MenuItem } from '@material-ui/core';
+import { Card, Button, CircularProgress, CardActions, CardContent, FormControl, InputLabel, Select, Typography, Grid, TextField, MenuItem, FormControlLabel, Checkbox } from '@material-ui/core';
import { Delete } from '@material-ui/icons';
import { makeStyles } from '@material-ui/core/styles';
@@ -41,25 +41,28 @@ class View extends Component{
this.handleRun = this.handleRun.bind(this);
this.handleRemoveObj = this.handleRemoveObj.bind(this);
+ this.handleCheckChange = this.handleCheckChange.bind(this);
+ this.makeNetworkUpdate = this.makeNetworkUpdate.bind(this);
+
this.handleAdd = this.handleAdd.bind(this);
this.handleChangeAddType = this.handleChangeAddType.bind(this);
}
componentDidMount(){
this.getTag();
- var intervalId = setInterval(() => {this.getTag(false)}, 5000);
- var timeoutId = setTimeout(() => {clearInterval(this.state.intervalId)}, 300000);
+ // var intervalId = setInterval(() => {this.getTag(false)}, 5000);
+ // var timeoutId = setTimeout(() => {clearInterval(this.state.intervalId)}, 300000);
- this.setState({
- intervalId: intervalId,
- timeoutId: timeoutId
- });
+ // this.setState({
+ // intervalId: intervalId,
+ // timeoutId: timeoutId
+ // });
}
- componentWillUnmount(){
- clearInterval(this.state.intervalId);
- clearTimeout(this.state.timeoutId);
- }
+ // componentWillUnmount(){
+ // clearInterval(this.state.intervalId);
+ // clearTimeout(this.state.timeoutId);
+ // }
getTag(error_toast = true){
axios.get(`/api/tag/${ this.state.tag_id }`)
@@ -105,6 +108,24 @@ class View extends Component{
}
+ handleCheckChange(event){
+ let payload = {...this.state.tag};
+ payload[event.target.name] = event.target.checked;
+
+ this.setState({tag: payload});
+
+ switch(event.target.name){
+ default:
+ this.makeNetworkUpdate({[event.target.name]: event.target.checked});
+ }
+ }
+
+ makeNetworkUpdate(changes){
+ axios.put(`/api/tag/${this.state.tag_id}`, changes).catch((error) => {
+ showMessage(`Error updating ${Object.keys(changes).join(", ")} (${error.response.status})`);
+ });
+ }
+
handleRun(event){
axios.get('/api/user')
.then((response) => {
@@ -256,13 +277,13 @@ class View extends Component{
{ this.state.tag.artists.length > 0 && Artists }
- { this.state.tag.artists.length > 0 && }
+ { this.state.tag.artists.length > 0 && }
{ this.state.tag.albums.length > 0 && Albums }
- { this.state.tag.albums.length > 0 && }
+ { this.state.tag.albums.length > 0 && }
{ this.state.tag.tracks.length > 0 && Tracks }
- { this.state.tag.tracks.length > 0 && }
+ { this.state.tag.tracks.length > 0 && }
-
+
+
+ }
+ label="Time Tag"
+ labelPlacement="bottom"
+ />
+
+
@@ -331,7 +361,7 @@ function ListBlock(props) {
alignItems="flex-start"
style={{padding: '24px'}}>
{props.list.map((music_obj) => )}
+ handler={ props.handler } addType={ props.addType } showTime={ props.showTime }/>)}
}
@@ -352,7 +382,12 @@ function BlockGridItem (props) {
}
{ 'count' in props.music_obj &&
- { props.music_obj.count }
+ 📈 { props.music_obj.count }
+
+ }
+ { 'time' in props.music_obj && props.showTime &&
+
+ 🕒 { props.music_obj.time }
}
@@ -375,11 +410,16 @@ function StatsCard (props) {
- = { props.count }
+ 📈 { props.count }
{ props.proportion.toFixed(2) }%
+ {props.showTime &&
+
+ 🕒 { props.time }
+
+ }