-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Last.fm support #196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Last.fm support #196
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
4747050
Added working lastfm support
semvis123 42e3d48
Added now playing to last-fm
semvis123 2bb67db
Fixed backwards compatibility
semvis123 1355b69
Remove the - Topic for more matches
semvis123 bffbcb2
Made the suffixes configurable and added VEVO suffix to remove
semvis123 493a583
Merge branch 'master' into lastfm
semvis123 f253a69
added comment for the added function
semvis123 bd82bd2
added extra comments and corrected existing ones
semvis123 0c6630d
additional cleanup/refactoring
semvis123 e1e8c94
Merge branch 'master' into lastfm
semvis123 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| const fetch = require('node-fetch'); | ||
| const md5 = require('md5'); | ||
| const open = require("open"); | ||
| const { setOptions } = require('../../config/plugins'); | ||
| const getSongInfo = require('../../providers/song-info'); | ||
| const defaultConfig = require('../../config/defaults'); | ||
|
|
||
| const cleanupArtistName = (config, artist) => { | ||
| // removes the suffixes of the artist name for more recognition by last.fm | ||
| const { suffixesToRemove } = config; | ||
| if (suffixesToRemove === undefined) return artist; | ||
|
|
||
| for (suffix of suffixesToRemove) { | ||
| artist = artist.replace(suffix, ''); | ||
| } | ||
| return artist; | ||
| } | ||
|
|
||
| const createFormData = params => { | ||
| // creates the body for in the post request | ||
| const formData = new URLSearchParams(); | ||
| for (key in params) { | ||
| formData.append(key, params[key]); | ||
| } | ||
| return formData; | ||
| } | ||
| const createQueryString = (params, api_sig) => { | ||
| // creates a querystring | ||
| const queryData = []; | ||
| params.api_sig = api_sig; | ||
| for (key in params) { | ||
| queryData.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`); | ||
| } | ||
| return '?'+queryData.join('&'); | ||
| } | ||
|
|
||
| const createApiSig = (params, secret) => { | ||
| // this function creates the api signature, see: https://www.last.fm/api/authspec | ||
| const keys = []; | ||
| for (key in params) { | ||
| keys.push(key); | ||
| } | ||
| keys.sort(); | ||
| let sig = ''; | ||
| for (key of keys) { | ||
| if (String(key) === 'format') | ||
| continue | ||
| sig += `${key}${params[key]}`; | ||
| } | ||
| sig += secret; | ||
| sig = md5(sig); | ||
| return sig; | ||
| } | ||
|
|
||
| const createToken = async ({ api_key, api_root, secret }) => { | ||
| // creates and stores the auth token | ||
| const data = { | ||
| method: 'auth.gettoken', | ||
| api_key: api_key, | ||
| format: 'json' | ||
| }; | ||
| const api_sig = createApiSig(data, secret); | ||
| let response = await fetch(`${api_root}${createQueryString(data, api_sig)}`); | ||
| response = await response.json(); | ||
| return response?.token; | ||
| } | ||
|
|
||
| const authenticate = async config => { | ||
| // asks the user for authentication | ||
| config.token = await createToken(config); | ||
| setOptions('last-fm', config); | ||
| open(`https://www.last.fm/api/auth/?api_key=${config.api_key}&token=${config.token}`); | ||
| return config; | ||
| } | ||
|
|
||
| const getAndSetSessionKey = async config => { | ||
| // get and store the session key | ||
| const data = { | ||
| api_key: config.api_key, | ||
| format: 'json', | ||
| method: 'auth.getsession', | ||
| token: config.token, | ||
| }; | ||
| const api_sig = createApiSig(data, config.secret); | ||
| let res = await fetch(`${config.api_root}${createQueryString(data, api_sig)}`); | ||
| res = await res.json(); | ||
| if (res.error) | ||
| await authenticate(config); | ||
| config.session_key = res?.session?.key; | ||
| setOptions('last-fm', config); | ||
| return config; | ||
| } | ||
|
|
||
| const postSongDataToAPI = async (songInfo, config, data) => { | ||
| // this sends a post request to the api, and adds the common data | ||
| if (!config.session_key) | ||
| await getAndSetSessionKey(config); | ||
|
|
||
| const postData = { | ||
| track: songInfo.title, | ||
| duration: songInfo.songDuration, | ||
| artist: songInfo.artist, | ||
| api_key: config.api_key, | ||
| sk: config.session_key, | ||
| format: 'json', | ||
| ...data, | ||
| }; | ||
|
|
||
| postData.api_sig = createApiSig(postData, config.secret); | ||
| fetch('https://ws.audioscrobbler.com/2.0/', {method: 'POST', body: createFormData(postData)}) | ||
| .catch(res => { | ||
| if (res.response.data.error == 9) { | ||
| // session key is invalid, so remove it from the config and reauthenticate | ||
| config.session_key = undefined; | ||
| setOptions('last-fm', config); | ||
| authenticate(config); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| const addScrobble = (songInfo, config) => { | ||
| // this adds one scrobbled song to last.fm | ||
| const data = { | ||
| method: 'track.scrobble', | ||
| timestamp: ~~((Date.now() - songInfo.elapsedSeconds) / 1000), | ||
| }; | ||
| postSongDataToAPI(songInfo, config, data); | ||
| } | ||
|
|
||
| const setNowPlaying = (songInfo, config) => { | ||
| // this sets the now playing status in last.fm | ||
| const data = { | ||
| method: 'track.updateNowPlaying', | ||
| }; | ||
| postSongDataToAPI(songInfo, config, data); | ||
| } | ||
|
|
||
|
|
||
| // this will store the timeout that will trigger addScrobble | ||
| let scrobbleTimer = undefined; | ||
|
|
||
| const lastfm = async (win, config) => { | ||
| const registerCallback = getSongInfo(win); | ||
|
|
||
| if (!config.api_root || !config.suffixesToRemove) { | ||
| // settings are not present, creating them with the default values | ||
| config = defaultConfig.plugins['last-fm']; | ||
| config.enabled = true; | ||
| setOptions('last-fm', config); | ||
| } | ||
|
|
||
| if (!config.session_key) { | ||
| // not authenticated | ||
| config = await getAndSetSessionKey(config); | ||
| } | ||
|
|
||
| registerCallback( songInfo => { | ||
| // set remove the old scrobble timer | ||
| clearTimeout(scrobbleTimer); | ||
| // make the artist name a bit cleaner | ||
| songInfo.artist = cleanupArtistName(config, songInfo.artist); | ||
| if (!songInfo.isPaused) { | ||
| setNowPlaying(songInfo, config); | ||
| // scrobble when the song is half way through, or has passed the 4 minute mark | ||
| const scrobbleTime = Math.min(Math.ceil(songInfo.songDuration / 2), 4 * 60); | ||
| if (scrobbleTime > songInfo.elapsedSeconds) { | ||
| // scrobble still needs to happen | ||
| const timeToWait = (scrobbleTime - songInfo.elapsedSeconds) * 1000; | ||
| scrobbleTimer = setTimeout(addScrobble, timeToWait, songInfo, config); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| module.exports = lastfm; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big deal but it seems the lock file is not up-to-date (axios is not used anymore),
yarnwill need to be run on the project to update it!