last.fm-to-mastodon.js(檔案已創建)
@@ -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() |
上一頁
下一頁