196 lines
6.3 KiB
Org Mode
196 lines
6.3 KiB
Org Mode
:PROPERTIES:
|
|
:ID: cce/npbot
|
|
:ROAM_REFS: https://botsin.space/@rrixlistensto https://github.com/barzamin/npbot
|
|
:ROAM_ALIASES: npbot
|
|
:END:
|
|
#+TITLE: My "Now Playing" on Fediverse
|
|
#+filetags: :Project:CCE:
|
|
|
|
#+PROPERTY: header-args :mkdirp yes
|
|
|
|
,#+CCE_ANSIBLE: npbot
|
|
#+CCE_ROLES: endpoint container server
|
|
#+CCE_PRIORITY: 30
|
|
#+CCE_PREDICATE: t
|
|
|
|
#+ARCOLOGY_KEY: cce/server/npbot
|
|
#+ARCOLOGY_ALLOW_CRAWL: t
|
|
|
|
When I am active on the [[id:62538db5-d94a-47c3-9998-086ded91fd88][Fediverse]] and I am [[id:c17ac008-acd9-4619-b324-7a56dfa635c5][Intentionally Listening]] to some [[id:4946884c-9335-4970-a53c-cb989970dc41][Important Albums]], it's nice to be able to share those tracks with others. I amalgamate all my listening stats ("scrobbles") on a [[roam:Last.FM]] Pro account, except for some years where I was only using gnu.fm instances which don't exist any more. Anyways, this bot includes a youtube link, or a link to my [[roam:MPD Server]] HTTP stream.
|
|
|
|
#+begin_src dot :file data/scrobble-dataflow.png :cmd fdp
|
|
digraph {
|
|
|
|
subgraph cluster_0 {
|
|
label="server"
|
|
"server mpd" -> "server mpdscribble"
|
|
"npbot"
|
|
}
|
|
"server mpdscribble" -> "LastFM"
|
|
|
|
subgraph cluster_1 {
|
|
label="laptop"
|
|
"laptop mpd" -> "laptop mpdscribble"
|
|
}
|
|
"laptop mpdscribble" -> "LastFM"
|
|
|
|
subgraph cluster_2 {
|
|
label="phone"
|
|
"notifications" -> "scrooble"
|
|
}
|
|
scrooble -> LastFM
|
|
|
|
LastFM -> npbot -> "botsin.space"
|
|
}
|
|
#+end_src
|
|
|
|
#+results:
|
|
[[file:data/scrobble-dataflow.png]]
|
|
|
|
* NEXT fix npbot...
|
|
* Container build using [[id:ea955c0f-a757-4f1f-ac80-fc2248c33e4b][Ansible Bender]] and [[id:cce/podman][Podman]]
|
|
|
|
This generates a dynamic playbook and then tells you how to run it:
|
|
|
|
#+begin_src shell :results drawer :exports code
|
|
cat <<EOF | python ~/org/cce/make-container-playbook.py
|
|
template: ~/org/cce/containers/simple.yml
|
|
friendly: Now Playing on Fediverse
|
|
service_name: npbot
|
|
build_roles:
|
|
- nowplaying-bot-build
|
|
build_reqs:
|
|
- python-devel
|
|
- python-pip
|
|
- libffi-devel
|
|
- python3-cffi
|
|
cmd: ananas /config.cfg
|
|
task_tags:
|
|
- comms
|
|
- media
|
|
- npbot
|
|
EOF
|
|
#+end_src
|
|
|
|
#+results:
|
|
:results:
|
|
[[shell:ANSIBLE_ROLES_PATH=~/org/cce/roles ansible-bender build /home/rrix/org/cce/containers/npbot/build.yml &][Execute =ANSIBLE_ROLES_PATH=~/org/cce/roles ansible-bender build /home/rrix/org/cce/containers/npbot/build.yml &=]]
|
|
:end:
|
|
|
|
#+begin_src yaml :tangle roles/nowplaying-bot-build/tasks/main.yml :mkdirp yes
|
|
- name: requirements copied in
|
|
copy:
|
|
src: requirements.txt
|
|
dest: "{{build_dir}}/requirements.txt"
|
|
|
|
- name: python dependencies installed
|
|
pip:
|
|
state: present
|
|
requirements: "{{build_dir}}/requirements.txt"
|
|
|
|
- name: files installed
|
|
copy:
|
|
src: "{{item}}"
|
|
dest: /
|
|
with_items:
|
|
- nowplaying.py
|
|
- config.cfg
|
|
#+end_src
|
|
|
|
* Nowplaying plugin source for [[roam:Ananas]]
|
|
|
|
#+begin_src txt :tangle containers/npbot/requirements.txt :mkdirp yes :comments none
|
|
pylast==2.1.0
|
|
ananas==1.0.0b17
|
|
requests==2.18.4
|
|
#+end_src
|
|
|
|
#+begin_src python :tangle containers/npbot/nowplaying.py :mkdirp yes
|
|
import requests
|
|
from pylast import LastFMNetwork
|
|
from ananas import PineappleBot, ConfigurationError, interval
|
|
|
|
def search_youtube(q, max_results=1, key=None):
|
|
if not key:
|
|
raise ValueError('search_youtube() requires a key to authorize with the Youtube v3 API')
|
|
|
|
r = requests.get('https://www.googleapis.com/youtube/v3/search',
|
|
params={'q': q, 'type': 'video', 'maxResults': max_results,
|
|
'part': 'snippet', 'key': key})
|
|
|
|
items = []
|
|
for item in r.json().get('items', []):
|
|
items.append({'id': item['id']['videoId'], 'title': item['snippet']['title']})
|
|
|
|
return items
|
|
|
|
class NowplayingBot(PineappleBot):
|
|
def init(self):
|
|
self.last_posted_track = None
|
|
|
|
def start(self):
|
|
for k in ['lastfm_api_key', 'lastfm_api_secret', 'lastfm_username', 'lastfm_password_hash',
|
|
'youtube_key', 'use_last_played']:
|
|
if k not in self.config:
|
|
raise ConfigurationError(f"NowplayingBot requires a '{k}'")
|
|
|
|
self.lastfm = LastFMNetwork(api_key=self.config.lastfm_api_key, api_secret=self.config.lastfm_api_secret,
|
|
username=self.config.lastfm_username,
|
|
password_hash=self.config.lastfm_password_hash)
|
|
|
|
self.post_np()
|
|
|
|
@interval(30)
|
|
def post_np(self):
|
|
# grab the track from the last.fm api
|
|
if self.config.use_last_played == 'yes':
|
|
currently_playing = self.lastfm.get_user(self.config.lastfm_username).get_recent_tracks(1)[0][0]
|
|
else:
|
|
currently_playing = self.lastfm.get_user(self.config.lastfm_username).get_now_playing()
|
|
|
|
# don't try to post if nothing is being played
|
|
if currently_playing is None:
|
|
return
|
|
|
|
# don't repost if we've already posted about this track
|
|
if currently_playing.__hash__() == self.last_posted_track:
|
|
return
|
|
else:
|
|
self.last_posted_track = currently_playing.__hash__()
|
|
|
|
# make a best-effort guess at the youtube link for this track
|
|
yt_search = search_youtube(str(currently_playing), key=self.config.youtube_key)
|
|
if len(yt_search) > 0:
|
|
yt_link = f"https://www.youtube.com/watch?v={yt_search[0]['id']}"
|
|
else:
|
|
yt_link = '🎇 no youtube link, sorry 🎇'
|
|
|
|
# template the post
|
|
post_template = '''\
|
|
#np #nowplaying {artist} - {track}
|
|
|
|
{yt_link} - https://music.fontkeming.fail/mpd (live)
|
|
'''
|
|
|
|
post = post_template.format(artist=currently_playing.get_artist().get_name(properly_capitalized=True),
|
|
track=currently_playing.get_title(), yt_link=yt_link)
|
|
|
|
# do the thing
|
|
self.mastodon.status_post(post, visibility='unlisted')
|
|
#+end_src
|
|
|
|
#+begin_src ini :tangle containers/npbot/config.cfg :comments none
|
|
[np]
|
|
class = nowplaying.NowplayingBot
|
|
domain = botsin.space
|
|
client_id = 5cd455c225935b1fa0585cc48e81a2985c41a0517330a3e39d30aa5a437e930e
|
|
client_secret = 7c645a9444d55f11f1f6f42bbd2fa7e611207e0045b1cbc789a67280885037cb
|
|
access_token = eaeaae60f1feebba5d7fc27c8b43ed0159ab5c06f20b52d4a27eabfa1e3b2bff
|
|
lastfm_api_key = da6e1d6f81f6813500c5900de742e06c
|
|
lastfm_api_secret = 915f9884d0868f19529ec25bf59d6718
|
|
lastfm_username = aes256thetic
|
|
lastfm_password_hash = ce92be174edf74689ce89d48b1613267
|
|
youtube_key = AIzaSyCJhDRlK5qzisk_dvv-crGe54EOd2bjfoI
|
|
use_last_played = no
|
|
#+end_src
|