Skip to content

Commit

Permalink
Merge pull request #245 from T-101/module-spotify-noapi
Browse files Browse the repository at this point in the history
Module spotify noapi
  • Loading branch information
lepinkainen committed Apr 14, 2018
2 parents 3d1487c + 62d0a84 commit aef0221
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 0 deletions.
File renamed without changes.
154 changes: 154 additions & 0 deletions pyfibot/modules/module_spotify_noapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""
Parse spotify URLs
A modified version of the original pyfibot spotify module
Spotify closed down the API and it needs authentication. This works around it by making a request to
an endpoint with a smartphone user-agent. It seems to yield more html-content from which to parse from
Use freely and at own risk. I am not viable for anything.
done in Jan 2018 by T-101 / Darklite ^ Primitive
"""

from __future__ import unicode_literals, print_function, division
import re
import json
import logging

from bs4 import BeautifulSoup

log = logging.getLogger('spotify')


def handle_privmsg(bot, user, channel, args):
class SpotifyData(object):

def __init__(self):
self._data = None
self.output = None

@staticmethod
def _parse_script_elements(scripts):
"""
Selects the desired <script> element containing the desired json data
"""
for script in scripts:
if re.search(r'Spotify.Entity', script.string):
return re.sub(r'[\n\t]', '', script.string)

@staticmethod
def _ms_human_readable(ms):
"""
Converts milliseconds into a tuple of (minutes, seconds)
"""
minutes = int(ms / 60000)
seconds = int((ms - minutes * 60000) / 1000)
return minutes, seconds

@property
def data(self):
return self._data

@data.setter
def data(self, value):
"""
Takes a list of <script> elements, selects the desired one and morphs contents to a neat json dict
"""
soup = BeautifulSoup(value, 'html.parser')
script = self._parse_script_elements(soup.find_all('script'))
if script:
element = re.search(r'Spotify.Entity = ({.*});', script).group(1)
self._data = json.loads(element)

@data.deleter
def data(self):
self._data = None
self.data = None

def parse(self, data, data_type):
"""
Parse data, and set the .output parameter accordingly
"""

self.data = data
if not self.data:
return

if data_type == "track":
artist = ', '.join([artist.get('name') for artist in self.data.get('artists')])
name = self.data.get('name')
duration = self._ms_human_readable(int(self.data.get('duration_ms')))

self.output = "\002[Spotify]\002 %s - %s [%sm%ss]" % (artist, name, duration[0], duration[1])
if self.data.get('album') and self.data.get('album').get('name'):
self.output += ' (from "%s")' % self.data.get('album').get('name')

if data_type == "album":
artist = ', '.join([artist.get('name') for artist in self.data.get('artists')])
name = self.data.get('name')
tracks = self.data.get('tracks').get('total')
released = 0
if re.match(r'\d{4}', self.data.get('release_date')):
released = re.match(r'\d{4}', self.data.get('release_date')).group()

self.output = "\002[Spotify Album]\002 %s (%s, %s tracks) by %s" % (name, released, tracks, artist)

if data_type == "artist":
self.output = "\002[Spotify Artist]\002 %s" % self.data.get('name')

if data_type == "playlist":
name = self.data.get('name')
owner = self.data.get('owner').get('id')
followers = self.data.get('followers').get('total')
tracks = self.data.get('tracks').get('total')

self.output = "\002[Spotify Playlist]\002 %s by %s (%s tracks, %s followers)" % (
name, owner, tracks, followers
)

return self

m = re.match(
".*(https?:\/\/open.spotify.com\/|spotify:)(?P<item>album|artist|track|user[:\/]\S+[:\/]playlist)[:\/](?P<id>[a-zA-Z0-9]+)\/?.*",
args)
if not m:
return None

spotify_id = m.group('id')
item = m.group('item').replace(':', '/').split('/')

if item[-1] not in ("track", "album", "artist", "playlist"):
bot.log("Given endpoint not yet supported")
return

r = re.search(r'(https?:\/\/\S+)', args)
if r:
url = r.group()
elif item[-1] == "playlist":
url = "https://open.spotify.com/user/%s/playlist/%s" % (item[1], spotify_id)
else:
url = "https://open.spotify.com/%s/%s" % (item[-1], spotify_id)

user_agent = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; SM-G900R4 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36'
}
r = bot.get_url(url=url, headers=user_agent)
if r.status_code != 200:
bot.log("module_spotify_noapi.py HTTP status code %s" % r.status_code)
# bot.log("HTTP response %s" % r.content)
return

spotify_data = SpotifyData().parse(r.content, item[-1])

if spotify_data.output:
bot.say(channel, spotify_data.output)

0 comments on commit aef0221

Please sign in to comment.