Ruby-Cogs/lastfm/top.py
2025-03-26 10:59:44 -04:00

360 lines
14 KiB
Python

import asyncio
import discord
from redbot.core.utils.chat_formatting import escape
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
from .abc import MixinMeta
from .exceptions import *
from .fmmixin import FMMixin
command_fm = FMMixin.command_fm
command_fm_server = FMMixin.command_fm_server
class TopMixin(MixinMeta):
"""Top Artist/Album/Track Commands"""
@command_fm.command(name="topartists", aliases=["ta"], usage="[timeframe] [amount]")
async def command_topartists(self, ctx, *args):
"""Most listened artists."""
conf = await self.config.user(ctx.author).all()
self.check_if_logged_in(conf)
async with ctx.typing():
arguments = self.parse_arguments(args)
data = await self.api_request(
ctx,
{
"user": conf["lastfm_username"],
"method": "user.gettopartists",
"period": arguments["period"],
"limit": arguments["amount"],
},
)
user_attr = data["topartists"]["@attr"]
artists = data["topartists"]["artist"]
if not artists:
return await ctx.send("You have not listened to any artists yet!")
rows = []
for i, artist in enumerate(artists, start=1):
name = escape(artist["name"], formatting=True)
plays = artist["playcount"]
rows.append(f"`#{i:2}` **{plays}** {self.format_plays(plays)} — **{name}**")
image_url = await self.scrape_artist_image(artists[0]["name"], ctx)
content = discord.Embed(color=await self.bot.get_embed_color(ctx.channel))
content.set_thumbnail(url=image_url)
content.set_footer(text=f"Total unique artists: {user_attr['total']}")
content.set_author(
name=f"{user_attr['user']}{self.humanized_period(arguments['period']).capitalize()} top artists",
icon_url=ctx.message.author.display_avatar.url,
)
pages = await self.create_pages(content, rows)
if len(pages) > 1:
await menu(ctx, pages[:15], DEFAULT_CONTROLS)
else:
await ctx.send(embed=pages[0])
@command_fm.command(name="topalbums", aliases=["talb"], usage="[timeframe] [amount]")
async def command_topalbums(self, ctx, *args):
"""Most listened albums."""
conf = await self.config.user(ctx.author).all()
self.check_if_logged_in(conf)
arguments = self.parse_arguments(args)
data = await self.api_request(
ctx,
{
"user": conf["lastfm_username"],
"method": "user.gettopalbums",
"period": arguments["period"],
"limit": arguments["amount"],
},
)
user_attr = data["topalbums"]["@attr"]
albums = data["topalbums"]["album"]
if not albums:
return await ctx.send("You have not listened to any albums yet!")
rows = []
for i, album in enumerate(albums, start=1):
name = escape(album["name"], formatting=True)
artist_name = escape(album["artist"]["name"], formatting=True)
plays = album["playcount"]
rows.append(
f"`#{i:2}` **{plays}** {self.format_plays(plays)} — **{artist_name}** — ***{name}***"
)
image_url = albums[0]["image"][-1]["#text"]
content = discord.Embed(color=await self.bot.get_embed_color(ctx.channel))
content.set_thumbnail(url=image_url)
content.set_footer(text=f"Total unique albums: {user_attr['total']}")
content.set_author(
name=f"{user_attr['user']}{self.humanized_period(arguments['period']).capitalize()} top albums",
icon_url=ctx.message.author.display_avatar.url,
)
pages = await self.create_pages(content, rows)
if len(pages) > 1:
await menu(ctx, pages[:15], DEFAULT_CONTROLS)
else:
await ctx.send(embed=pages[0])
@command_fm.command(name="toptracks", aliases=["tt"], usage="[timeframe] [amount]")
async def command_toptracks(self, ctx, *args):
"""Most listened tracks."""
conf = await self.config.user(ctx.author).all()
self.check_if_logged_in(conf)
async with ctx.typing():
arguments = self.parse_arguments(args)
data = await self.api_request(
ctx,
{
"user": conf["lastfm_username"],
"method": "user.gettoptracks",
"period": arguments["period"],
"limit": arguments["amount"],
},
)
user_attr = data["toptracks"]["@attr"]
tracks = data["toptracks"]["track"]
if not tracks:
return await ctx.send("You have not listened to anything yet!")
rows = []
for i, track in enumerate(tracks, start=1):
name = escape(track["name"], formatting=True)
artist_name = escape(track["artist"]["name"], formatting=True)
plays = track["playcount"]
rows.append(
f"`#{i:2}` **{plays}** {self.format_plays(plays)} — **{artist_name}** — ***{name}***"
)
trackdata = await self.api_request(
ctx,
{
"user": name,
"method": "track.getInfo",
"artist": tracks[0]["artist"]["name"],
"track": tracks[0]["name"],
},
)
content = discord.Embed(color=await self.bot.get_embed_color(ctx.channel))
try:
if trackdata is None:
raise KeyError
image_url = trackdata["track"]["album"]["image"][-1]["#text"]
# image_url_small = trackdata['track']['album']['image'][1]['#text']
# image_colour = await color_from_image_url(image_url_small)
except KeyError:
image_url = await self.scrape_artist_image(tracks[0]["artist"]["name"], ctx)
# image_colour = await color_from_image_url(image_url)
content.set_thumbnail(url=image_url)
content.set_footer(text=f"Total unique tracks: {user_attr['total']}")
content.set_author(
name=f"{user_attr['user']}{self.humanized_period(arguments['period']).capitalize()} top tracks",
icon_url=ctx.message.author.display_avatar.url,
)
pages = await self.create_pages(content, rows)
if len(pages) > 1:
await menu(ctx, pages[:15], DEFAULT_CONTROLS)
else:
await ctx.send(embed=pages[0])
@command_fm_server.command(name="topartists", aliases=["ta"])
async def command_servertopartists(self, ctx):
"""Most listened artists in the server."""
tasks = []
userlist = await self.config.all_users()
guildusers = [x.id for x in ctx.guild.members]
userslist = [user for user in userlist if user in guildusers]
for user in userslist:
lastfm_username = userlist[user]["lastfm_username"]
if lastfm_username is None:
continue
member = ctx.guild.get_member(user)
if member is None:
continue
tasks.append(
self.get_server_top(
ctx,
lastfm_username,
"artist",
"overall",
100,
)
)
if not tasks:
return await ctx.send("No users have logged in to LastFM!")
async with ctx.typing():
mapping = {}
total_users = 0
total_plays = 0
data = await asyncio.gather(*tasks)
for user in data:
if user is None:
continue
total_users += 1
for user_data in user:
artist_name = user_data["name"]
artist_plays = int(user_data["playcount"])
total_plays += artist_plays
if artist_name in mapping:
mapping[artist_name] += artist_plays
else:
mapping[artist_name] = artist_plays
rows = []
for i, (artist, playcount) in enumerate(
sorted(mapping.items(), key=lambda x: x[1], reverse=True), start=1
):
name = escape(artist, formatting=True)
plays = playcount
rows.append(f"`#{i:2}` **{plays}** {self.format_plays(plays)} — **{name}**")
content = discord.Embed(
title=f"Most listened to artists in {ctx.guild}",
color=await self.bot.get_embed_color(ctx.channel),
)
content.set_footer(text=f"Top 100 artists of {total_users} users.")
pages = await self.create_pages(content, rows)
if len(pages) > 1:
await menu(ctx, pages[:15], DEFAULT_CONTROLS)
else:
await ctx.send(embed=pages[0])
@command_fm_server.command(name="topalbums", aliases=["talb"])
async def command_servertopalbums(self, ctx):
"""Most listened albums in the server."""
tasks = []
userlist = await self.config.all_users()
guildusers = [x.id for x in ctx.guild.members]
userslist = [user for user in userlist if user in guildusers]
for user in userslist:
lastfm_username = userlist[user]["lastfm_username"]
if lastfm_username is None:
continue
member = ctx.guild.get_member(user)
if member is None:
continue
tasks.append(
self.get_server_top(
ctx,
lastfm_username,
"album",
"overall",
100,
)
)
if not tasks:
return await ctx.send("No users have logged in to LastFM!")
async with ctx.typing():
mapping = {}
total_users = 0
total_plays = 0
data = await asyncio.gather(*tasks)
for user in data:
if user is None:
continue
total_users += 1
for user_data in user:
name = f'**{escape(user_data["artist"]["name"], formatting=True)}** — **{escape(user_data["name"], formatting=True)}**'
plays = int(user_data["playcount"])
total_plays += plays
if name in mapping:
mapping[name] += plays
else:
mapping[name] = plays
rows = []
for i, (album, playcount) in enumerate(
sorted(mapping.items(), key=lambda x: x[1], reverse=True), start=1
):
plays = playcount
rows.append(f"`#{i:2}` **{plays}** {self.format_plays(plays)}{album}")
content = discord.Embed(
title=f"Most listened to albums in {ctx.guild}",
color=await self.bot.get_embed_color(ctx.channel),
)
content.set_footer(text=f"Top 100 albums of {total_users} users.")
pages = await self.create_pages(content, rows)
if len(pages) > 1:
await menu(ctx, pages[:15], DEFAULT_CONTROLS)
else:
await ctx.send(embed=pages[0])
@command_fm_server.command(name="toptracks", aliases=["tt"])
async def command_servertoptracks(self, ctx):
"""Most listened tracks in the server."""
tasks = []
userlist = await self.config.all_users()
guildusers = [x.id for x in ctx.guild.members]
userslist = [user for user in userlist if user in guildusers]
for user in userslist:
lastfm_username = userlist[user]["lastfm_username"]
if lastfm_username is None:
continue
member = ctx.guild.get_member(user)
if member is None:
continue
tasks.append(
self.get_server_top(
ctx,
lastfm_username,
"track",
"overall",
100,
)
)
if not tasks:
return await ctx.send("No users have logged in to LastFM!")
async with ctx.typing():
mapping = {}
total_users = 0
total_plays = 0
data = await asyncio.gather(*tasks)
for user in data:
if user is None:
continue
total_users += 1
for user_data in user:
name = f'**{escape(user_data["artist"]["name"], formatting=True)}** — **{escape(user_data["name"], formatting=True)}**'
plays = int(user_data["playcount"])
total_plays += plays
if name in mapping:
mapping[name] += plays
else:
mapping[name] = plays
rows = []
for i, (track, playcount) in enumerate(
sorted(mapping.items(), key=lambda x: x[1], reverse=True), start=1
):
plays = playcount
rows.append(f"`#{i:2}` **{plays}** {self.format_plays(plays)}{track}")
content = discord.Embed(
title=f"Most listened to tracks in {ctx.guild}",
color=await self.bot.get_embed_color(ctx.channel),
)
content.set_footer(text=f"Top 100 tracks of {total_users} users.")
pages = await self.create_pages(content, rows)
if len(pages) > 1:
await menu(ctx, pages[:15], DEFAULT_CONTROLS)
else:
await ctx.send(embed=pages[0])