Page MenuHomePhorge

No OneTemporary

diff --git a/get_date.py b/get_date.py
new file mode 100755
index 0000000..ff61b20
--- /dev/null
+++ b/get_date.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+import sys
+import tekore as tk
+import libmusicleague
+import pyml_config as config
+import json
+
+# Set up Spotify connection using client token
+conf = (config.client_id, config.client_secret)
+token = tk.request_client_token(*conf)
+spotify = tk.Spotify(token)
+
+# Get track ID from first argument
+arg = sys.argv[1]
+
+stype = libmusicleague.spotify_type(arg)
+
+if stype == "track":
+ track = libmusicleague.return_spotify_track(arg)
+ json_object = json.loads(track.json())
+ releasedate = json_object['album']['release_date']
+ trackname = json_object['name'] + " by " + json_object['artists'][0]['name']
+ spiel = trackname + " was released on " + releasedate
+elif stype == "album":
+ tu = libmusicleague.spotify_type_and_id(arg) # get tuple
+ album = spotify.album(tu[1]) # second element of tuple is ID
+ releasedate = album.asbuiltin()['release_date']
+ fullname = album.asbuiltin()['name'] + " by " + album.asbuiltin()['artists'][0]['name']
+ spiel = fullname + " was released on " + releasedate
+
+print(spiel)
diff --git a/libmusicleague.py b/libmusicleague.py
index ecc41c6..fce1068 100644
--- a/libmusicleague.py
+++ b/libmusicleague.py
@@ -1,261 +1,298 @@
#!/usr/bin/env python3
from collections import OrderedDict # For sorting the Hot 100
import tekore as tk
import pyml_config as config
import json
import re
import requests
from typing import Union # For CompactJSONEncoder
conf = (config.client_id, config.client_secret)
token = tk.request_client_token(*conf)
spotify = tk.Spotify(token)
## Takes a trackid URI and turns it into a track object
# \param uri A Spotify trackid, in the form of a URI (ie. spotify:track:SOMEUUID)
def track_from_id(uri):
track = spotify.track(trackid)
return track
def trackname(track):
name = track.asbuiltin().get('name')
return name
## Takes a track and tries to determine the primary artist on that track
# \param track A Spotify track
# \return artist A string that's hopefully the primary artist's name
def primary_artist(track):
if track is None:
#print(bcolors.FAIL+"Uhh, nothing here, skipping, hope that's cool!"+bcolors.ENDC)
return
#print(bcolors.HEADER+"Now checking this track: "+track.asbuiltin().get("name")+bcolors.ENDC)
albuminfo = track.asbuiltin().get("album")
albumartist = albuminfo.get("artists")[0]["name"] #TODO: Handle when an album has more than one artist!
#print("Album artist is "+albumartist)
#print(track.asbuiltin())
#print(albuminfo)
artistlist = []
for a in track.artists:
artistlist += [a.asbuiltin().get("name")]
#print("Found artist "+a.asbuiltin().get("name"))
if albumartist in artistlist:
#print("One of the track's artists is also the album artist, "+albumartist)
if len(track.artists) == 1:
artist = albumartist
else:
#print("Well, there's other artists, but going with the one whose name is on the album...")
artist = track.artists[0].asbuiltin().get("name")
else:
#print(bcolors.WARNING+"Danger, Will Robinson! No track artist matches the album artist "+albumartist+"!"+bcolors.ENDC)
if "Various Artists" in albumartist:
#print("Taking 'Various Artists' as evidence that this is some sort of compilation, and defaulting to the first track artist listed")
artist = track.artists[0].asbuiltin().get("name")
elif "Cast" in albumartist:
#print("Album artist has 'Cast' in its name, presuming this is a musical and that'll make more sense than crediting the individual performer as is weirdly common")
artist = albumartist
elif "Cast" in albuminfo["name"]:
#print("Album title has 'Cast' in its name, presuming this is a musical and that'll make more sense than crediting the individual performer as is weirdly common")
artist = albumartist
else:
# If we can't match anything else, default to track artist
artist = track.artists[0].asbuiltin().get("name")
#print(bcolors.FAIL+"Oh no! Seems like I didn't catch a potential scenario, couldn't determine artist for "+track.asbuiltin().get("name")+bcolors.ENDC)
#print(" Going with "+bcolors.OKCYAN+artist+bcolors.ENDC)
return artist
# From Blender by way of https://stackoverflow.com/a/287944/2808933, should do something better
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
## Takes a URL or URI and figures out if it's a URL or a URI
# \param ur A Spotify URL or URI
def ur_type(ur):
spotify_url_prefix = "https://open.spotify.com/"
spotify_uri_prefix = "spotify:"
spotify_new_prefix = "https://spotify.link"
u = "unknown"
if (ur.startswith(spotify_url_prefix)):
u = "url"
elif (ur.startswith(spotify_uri_prefix)):
u = "uri"
elif (ur.startswith(spotify_new_prefix)):
u = "new"
return u
## Translate new Spotify URL format into full verbose ID
# \param newurl A Spotify URL of the new type
def newurl_expand(newurl):
req = requests.get(newurl)
# If Spotify just actually used HTTP like God intended, we could just use
# return(r.url)
# Intead, we're gonna have to parse the page text for the URL to the track that their JavaScript will do the reload for (kindof insane, on a desktop you don't even see the page)
pagetext = req.text
r = re.search('validateProtocol\("https://open.spotify.com/(.*)\?', pagetext)
url = "https://open.spotify.com/"+r[1]
return(url)
## Takes a URL or URI and figures out if it's a URL or a URI, then figures out and returns what type of entity (track, album, playlist, artist, whatever) it is
def spotify_type(ur):
types = ["playlist", "album", "track", "artist"]
this_is = "dunno"
# Test UR* type and use tk and lml functions accordingly
ut = ur_type(ur)
if ut == "uri":
f = tk.from_uri(ur)
this_is = f[0]
elif ut == "url":
f = tk.from_url(ur)
this_is = f[0]
elif ut == "new":
ur = newurl_expand(ur)
f = tk.from_url(ur)
this_is = f[0]
# for t in types:
# if (("spotify:"+t+":" in ur) or ("spotify.com/"+t+"/" in ur)):
# this_is = t
return this_is
+## Takes a URL or URI and figures out if it's a URL or a URI, then figures out and returns what type of entity (track, album, playlist, artist, whatever) it is alongside the ID (there's probably a Tekore function I'm missing that does this)
+def spotify_type_and_id(ur):
+ types = ["playlist", "album", "track", "artist"]
+ this_is = "dunno"
+
+ # Test UR* type and use tk and lml functions accordingly
+ ut = ur_type(ur)
+ if ut == "uri":
+ f = tk.from_uri(ur)
+ elif ut == "url":
+ f = tk.from_url(ur)
+ elif ut == "new":
+ ur = newurl_expand(ur)
+ f = tk.from_url(ur)
+
+ return f
+
+
+# Takes a UR*, determines and deciphers type, then returns a track object
+def return_spotify_track(ur):
+ # Test UR* type and use tk and lml functions accordingly
+ ut = ur_type(ur)
+ if ut == "uri":
+ f = tk.from_uri(ur)
+ elif ut == "url":
+ f = tk.from_url(ur)
+ elif ut == "new":
+ f = tk.from_url(newurl_expand(ur))
+
+ if f[0] == "track":
+ t = spotify.track(f[1])
+ else:
+ print("not a track?")
+ return t
+
+
+
def play_list(uri):
info = spotify.playlist(uri)
playlist_id = uri
title = info.asbuiltin().get("name")
tracks = spotify.playlist_items(playlist_id)
tracks = spotify.all_items(tracks)
items = []
for t in tracks:
#print(t.track.asbuiltin().get('name'))
artist = primary_artist(t.track)
items.append(t)
return title, items
def playlist_as_textlist(playlist):
l = []
distinction = " - "
for i in playlist[1]:
name = trackname(i.track)
artist = primary_artist(i.track)
l.append(artist+distinction+name)
return l
def print_playlist(playlist):
l = []
title = playlist[0]
distinction = " - "
titleprefix = "###"
itemprefix = "####"
for i in playlist[1]:
name = trackname(i.track)
artist = primary_artist(i.track)
l.append(artist+distinction+name)
print(titleprefix+' '+title, end = '')
print('', *l, sep='\n'+itemprefix+' ')
def print_hot(playlist):
l = []
title = playlist[0]
distinction = " - "
for i in playlist[1]:
l.append([f'{i.track.popularity:03d}'+"\t"+primary_artist(i.track)+"\t"+trackname(i.track)])
# l.append([f'{i.track.popularity:03d}')+" | "+trackname(i.track)+" by "+primary_artist(i.track)])
# l.sort(key=operator.itemgetter('popularity'))
# print(titleprefix+' '+title, end = '')
# print('', *l, sep='\n'+itemprefix+' ')
# print(l)
l.sort()
#print('', *l, sep='\n')
for line in l:
print(line[0])
# From https://gist.github.com/jannismain/e96666ca4f059c3e5bc28abb711b5c92 because I want inner lists not to have newlines splattered about
class CompactJSONEncoder(json.JSONEncoder):
"""A JSON Encoder that puts small containers on single lines."""
CONTAINER_TYPES = (list, tuple, dict)
"""Container datatypes include primitives or other containers."""
MAX_WIDTH = 10000
"""Maximum width of a container that might be put on a single line."""
MAX_ITEMS = 1000
"""Maximum number of items in container that might be put on single line."""
INDENTATION_CHAR = " "
def __init__(self, *args, **kwargs):
# using this class without indentation is pointless
if kwargs.get("indent") is None:
kwargs.update({"indent": 4})
super().__init__(*args, **kwargs)
self.indentation_level = 0
def encode(self, o):
"""Encode JSON object *o* with respect to single line lists."""
if isinstance(o, (list, tuple)):
if self._put_on_single_line(o):
return "[" + ", ".join(self.encode(el) for el in o) + "]"
else:
self.indentation_level += 1
output = [self.indent_str + self.encode(el) for el in o]
self.indentation_level -= 1
return "[\n" + ",\n".join(output) + "\n" + self.indent_str + "]"
elif isinstance(o, dict):
if o:
if self._put_on_single_line(o):
return "{ " + ", ".join(f"{self.encode(k)}: {self.encode(el)}" for k, el in o.items()) + " }"
else:
self.indentation_level += 1
output = [self.indent_str + f"{json.dumps(k)}: {self.encode(v)}" for k, v in o.items()]
self.indentation_level -= 1
return "{\n" + ",\n".join(output) + "\n" + self.indent_str + "}"
else:
return "{}"
elif isinstance(o, float): # Use scientific notation for floats, where appropiate
return format(o, "g")
elif isinstance(o, str): # escape newlines
o = o.replace("\n", "\\n")
return f'"{o}"'
else:
return json.dumps(o)
def iterencode(self, o, **kwargs):
"""Required to also work with `json.dump`."""
return self.encode(o)
def _put_on_single_line(self, o):
return self._primitives_only(o) and len(o) <= self.MAX_ITEMS and len(str(o)) - 2 <= self.MAX_WIDTH
def _primitives_only(self, o: Union[list, tuple, dict]):
if isinstance(o, (list, tuple)):
return not any(isinstance(el, self.CONTAINER_TYPES) for el in o)
elif isinstance(o, dict):
return not any(isinstance(el, self.CONTAINER_TYPES) for el in o.values())
@property
def indent_str(self) -> str:
return self.INDENTATION_CHAR*(self.indentation_level*self.indent)
diff --git a/spotify_releasedate.php b/spotify_releasedate.php
new file mode 100644
index 0000000..2e0bc95
--- /dev/null
+++ b/spotify_releasedate.php
@@ -0,0 +1,33 @@
+<html>
+
+<head>
+<title>Spotify album/track release date</title>
+</head>
+
+<body>
+
+
+<form action="" method="POST">
+<p><strong>Enter a Spotify track or album URL:</strong> <input name="URL" id="URL"></p>
+<p><button>Submit</button></p>
+</form>
+
+<?php
+
+if($_POST['URL']) {
+ $url = $_POST['URL'];
+ if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
+ die("Sorry, only valid URLs are currently supported. Take it up with Keith! He probably just forgot to make URIs work too. Or maybe what you pasted in isn't valid as either...");
+ }
+
+ # Run Python script (should rewrite in PHP sometime...)
+ $out = shell_exec("python3 get_date.py $url");
+
+ print("$out");
+
+
+?>
+
+</body>
+
+</html>

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jan 23, 11:36 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
131570
Default Alt Text
(12 KB)

Event Timeline