Last active 1741105193

melanie revised this gist 1741105193. Go to revision

No changes

melanie revised this gist 1732922294. Go to revision

No changes

melanie revised this gist 1732918382. Go to revision

1 file changed, 137 insertions

last.fm-to-mastodon.js(file created)

@@ -0,0 +1,137 @@
1 + // Variables used by Scriptable.
2 + // These must be at the very top of the file. Do not edit.
3 + // icon-color: pink; icon-glyph: headphones-alt;
4 + // Last.fm to Mastodon
5 + // by @zicklepop@nyan.lol
6 + //
7 + // Set a profile field to your currently playing track
8 + //
9 + // Requirements:
10 + // - API Key from Last.fm: https://www.last.fm/api
11 + // - Token from Mastodon with permission scopes for:
12 + // `read:account` and `write:account`
13 + // - Find it on your Mastodon instance website under
14 + // Settings -> Development -> New Application
15 +
16 + // Script Constants
17 + // - Ignore, just defining before use
18 + // Continue down to config
19 +
20 + const LASTFM_URL_IN_VALUE = 'LASTFM_URL_IN_VALUE'
21 + const LASTFM_IN_NAME = 'LASTFM_IN_NAME'
22 +
23 + // Config
24 + // - Put in your details below
25 + // - Find Field By:
26 + // - When set to LASTFM_URL_IN_VALUE the script will
27 + // look for a last.fm url in a field value and set
28 + // the title of the field to the current track
29 + // - When set to LASTFM_IN_NAME the script will look
30 + // for a field title that is `last.fm` (case-insenstive)
31 + // and set the value to the current track
32 +
33 + const LASTFM_USERNAME = ''
34 + const LASTFM_API_KEY = ''
35 + const MASTODON_INSTANCE = '' // ie: social.lol
36 + const MASTODON_TOKEN = ''
37 + const FIND_FIELD_BY = LASTFM_URL_IN_VALUE // or LASTFM_IN_NAME
38 +
39 + // More Script Constants
40 + // - You can ignore this too
41 +
42 + const LASTFM_API = `http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${LASTFM_USERNAME}&api_key=${LASTFM_API_KEY}&format=json&limit=1`
43 + const MASTODON_GET_ACCOUNT_API = `https://${MASTODON_INSTANCE}/api/v1/accounts/verify_credentials`
44 + const MASTODON_UPDATE_FIELDS_API = `https://${MASTODON_INSTANCE}/api/v1/accounts/update_credentials`
45 + const MASTODON_HEADERS = {
46 + 'Content-Type': 'application/json',
47 + Authorization: `Bearer ${MASTODON_TOKEN}`,
48 + }
49 +
50 + // Last FM Related Functions
51 +
52 + function formatTrackString(track) {
53 + const { artist, album, name } = track
54 + const albumName = album['#text']
55 + const artistName = artist['#text']
56 +
57 + // If you want to change the format of the track
58 + // value, you would do it here.
59 + return `${artistName} - ${albumName} - ${name}`
60 + }
61 +
62 + async function getLatestTrack() {
63 + const res = new Request(LASTFM_API)
64 + const data = await res.loadJSON()
65 + const track = data.recenttracks.track[0]
66 + return formatTrackString(track)
67 + }
68 +
69 + // Mastodon Related Functions
70 +
71 + function findProfileField(el, i) {
72 + const { name, value } = el
73 + if (FIND_FIELD_BY === LASTFM_URL_IN_VALUE) {
74 + return value.toLowerCase().indexOf('last.fm/user/') >= 0
75 + }
76 + if (FIND_FIELD_BY === LASTFM_IN_NAME) {
77 + return name.toLowerCase().indexOf('last.fm') >= 0
78 + }
79 + return false
80 + }
81 +
82 + function buildFieldParams(fields) {
83 + return fields.reduce((out, { name, value }, index) => {
84 + const prefix = index === 0 ? '?' : '&'
85 + return `${out}${prefix}fields_attributes[${index}][name]=${encodeURIComponent(
86 + name
87 + )}&fields_attributes[${index}][value]=${encodeURIComponent(value)}`
88 + }, '')
89 + }
90 +
91 + async function getFields() {
92 + const res = new Request(MASTODON_GET_ACCOUNT_API)
93 + res.headers = MASTODON_HEADERS
94 + const data = await res.loadJSON()
95 + const { source } = data
96 + const { fields } = source
97 + const targetField = fields.find(findProfileField)
98 + const index = fields.indexOf(targetField)
99 + return { index, fields }
100 + }
101 +
102 + async function updateMastodonField() {
103 + const latestTrack = await getLatestTrack()
104 + const { fields, index } = await getFields()
105 + let hasChange = false
106 +
107 + if (FIND_FIELD_BY === LASTFM_URL_IN_VALUE) {
108 + if (fields[index].name !== latestTrack) {
109 + hasChange = true
110 + fields[index].name = latestTrack
111 + }
112 + }
113 + if (FIND_FIELD_BY === LASTFM_IN_NAME) {
114 + if (fields[index].value !== latestTrack) {
115 + hasChange = true
116 + fields[index].value = latestTrack
117 + }
118 + }
119 +
120 + if (hasChange) {
121 + const apiUrl = `${MASTODON_UPDATE_FIELDS_API}${buildFieldParams(fields)}`
122 + const res = new Request(apiUrl)
123 + res.headers = MASTODON_HEADERS
124 + res.method = 'PATCH'
125 + const data = await res.loadJSON()
126 +
127 + if (data.error) {
128 + console.log(`Error: ${data.error}`)
129 + } else {
130 + console.log('Updated')
131 + }
132 + } else {
133 + console.log('No change')
134 + }
135 + }
136 +
137 + await updateMastodonField()
Newer Older