Skip to content

Commit

Permalink
Split fetchers into classes, used inheritance to provide cache
Browse files Browse the repository at this point in the history
  • Loading branch information
sdasda7777 authored and greendoescode committed May 26, 2024
1 parent b9b4025 commit 5c4755d
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 117 deletions.
29 changes: 29 additions & 0 deletions src/rpc/aCachingFetcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@


function createKey(metadata) {
// Create key that can be reasonably expected to be same for all songs in an album
return JSON.stringify({
artist: metadata.ALBUMARTIST || metadata.artist,
album: metadata.album,
date: metadata.date,
publisher: metadata.publisher,
});
}

class ACachingFetcher {

/** @type {!Object} #knownResults [metadataJSON][service] = {{fetchedFrom: string, artworkUrl: string, joinUrl: string}} */
#knownResults = {};

fetch(metadata) {
let metadataJSON = createKey(metadata);
// Use cached results if possible
if (metadataJSON in this.#knownResults){
return this.#knownResults[metadataJSON];
} else {
return (this.#knownResults[metadataJSON] = this.fetchUncached(metadata));
}
}
}

module.exports = ACachingFetcher;
37 changes: 37 additions & 0 deletions src/rpc/appleFetcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

const ACachingFetcher = require('./aCachingFetcher.js');
const axios = require('axios');

/**
* Caching fetcher for covers from iTunes
*/
class AppleFetcher extends ACachingFetcher {
/**
* Fetch artwork and join URLs if not cached, otherwise return cached
* @param {Object} metadata VLC metadata
* @returns {?{artworkFrom: ?string, artworkUrl: ?string, joinFrom: ?string, joinUrl: ?string}}
*/
async fetchUncached(metadata) {
if ((metadata.ALBUMARTIST || metadata.artist) && metadata.title) {
const result = await axios.get(
"https://itunes.apple.com/search",
{
params: {
media: "music",
term: `${metadata.ALBUMARTIST || metadata.artist} ${metadata.title}`,
},
headers: {"Accept-Encoding": "gzip,deflate,compress" }
});
if (result.data.resultCount > 0 && result.data.results[0] !== undefined) {
return {
artworkFrom: "Apple Music",
artworkUrl: result.data.results[0].artworkUrl100,
joinFrom: "Apple Music",
joinUrl: result.data.results[0].trackViewUrl
};
}
}
}
}

module.exports = AppleFetcher;
40 changes: 40 additions & 0 deletions src/rpc/bandcampFetcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

const ACachingFetcher = require('./aCachingFetcher.js');
const axios = require('axios');

/**
* Caching fetcher for covers from iTunes
*/
class BandcampFetcher extends ACachingFetcher {
/**
* Fetch artwork and join URLs if not cached, otherwise return cached
* @param {Object} metadata VLC metadata
* @returns {?{artworkFrom: ?string, artworkUrl: ?string, joinFrom: ?string, joinUrl: ?string}}
*/
async fetchUncached(metadata) {
if ((metadata.ALBUMARTIST || metadata.artist) && (metadata.album || metadata.title)) {
const result = await axios.post(
"https://bandcamp.com/api/bcsearch_public_api/1/autocomplete_elastic",
{
fan_id: null,
full_page: false,
search_filter: "",
search_text: `${metadata.ALBUMARTIST || metadata.artist} ${metadata.album || metadata.title}`,
},{
headers: {"Accept-Encoding": "gzip,deflate,compress" }
});
if (result.data.auto.results.length > 0) {
let resultItem = result.data.auto.results[0];
//console.log(resultTrack);
return {
artworkFrom: "Bandcamp",
artworkUrl: resultItem.img.replace("/img/", "/img/a"),
joinFrom: "Bandcamp",
joinUrl: resultItem.item_url_path
}
}
}
}
}

module.exports = BandcampFetcher;
34 changes: 34 additions & 0 deletions src/rpc/coverartarchiveFetcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

const ACachingFetcher = require('./aCachingFetcher.js');
const axios = require('axios');

/**
* Caching fetcher for covers from CoverArtArchive
*/
class CoverArtArchiveFetcher extends ACachingFetcher {
/**
* Fetch artwork and join URLs if not cached, otherwise return cached
* @param {Object} metadata VLC metadata
* @returns {?{artworkFrom: ?string, artworkUrl: ?string, joinFrom: ?string, joinUrl: ?string}}
*/
async fetchUncached(metadata) {
if ((metadata.MUSICBRAINZ_ALBUMID) && metadata.title) {
const result = await axios.get(
"https://coverartarchive.org/release/" + metadata.MUSICBRAINZ_ALBUMID,
{
headers: {"Accept-Encoding": "gzip,deflate,compress" }
});
if (result.data.images[0] !== undefined){
//console.warn(result.data.images[0].thumbnails.small);
return {
artworkFrom: "Cover Art Archive",
artworkUrl: result.data.images[0].thumbnails.small,
joinFrom: "Cover Art Archive",
joinUrl: result.data.release
};
}
}
}
}

module.exports = CoverArtArchiveFetcher;
130 changes: 13 additions & 117 deletions src/rpc/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,123 +8,23 @@ const log = require('../helpers/lager.js');
const cl = require('../helpers/configLoader.js');
const config = cl.getOrInit('config.js');
const axios = require('axios');
const path = require("path");
const { debug } = require('console');

let staticOverridesFetcher = new (require('./staticOverridesFetcher.js'))(cl.getOrInit('staticoverrides.js'));
let musichoardersFetcher = new (require('./musichoardersFetcher.js'))(config.rpc.persistentMusicHoardersCache);
let appleFetcher = new (require('./appleFetcher.js'))();
let bandcampFetcher = new (require('./bandcampFetcher.js'))();
let coverartarchiveFetcher = new (require('./coverartarchiveFetcher.js'))();

// These functions, 'fetchers', provide uniform inteface for simple access to the APIs
// They take VLC metadata as the argument and on success return object containing
// fetchedFrom, artworkUrl and joinUrl (or undefined where not applicable).
// In order to be as simple as possible, the functions may throw exceptions or not return anything.
// They are expected to be called through the `fetchSafely()` wrapper.
const fetchers = {
"staticoverrides": async (metadata) => {
return staticOverridesFetcher.fetch(metadata);
},
"musichoarders": async (metadata) => {
//return musichoardersFetcher.fetch("musichoarders", metadata);
},
"apple": async (metadata) => { // Doesn't rely on MusicHoarders, keep it that way, just in case
if ((metadata.ALBUMARTIST || metadata.artist) && metadata.title)
{
const result = await axios.get("https://itunes.apple.com/search", {
params: {
media: "music",
term: `${metadata.ALBUMARTIST || metadata.artist} ${metadata.title}`,
},
headers: {"Accept-Encoding": "gzip,deflate,compress" }
});
if (result.data.resultCount > 0 && result.data.results[0] !== undefined)
{
return {
artworkFrom: "Apple Music",
artworkUrl: result.data.results[0].artworkUrl100,
joinFrom: "Apple Music",
joinUrl: result.data.results[0].trackViewUrl
};
}
}
},
"bandcamp": async (metadata) => {
if ((metadata.ALBUMARTIST || metadata.artist) && (metadata.album || metadata.title))
{
const result = await axios.post("https://bandcamp.com/api/bcsearch_public_api/1/autocomplete_elastic", {
fan_id: null,
full_page: false,
search_filter: "",
search_text: `${metadata.ALBUMARTIST || metadata.artist} ${metadata.album || metadata.title}`,
},{
headers: {"Accept-Encoding": "gzip,deflate,compress" }
});
if (result.data.auto.results.length > 0)
{
let resultItem = result.data.auto.results[0];
//console.log(resultTrack);
return {
artworkFrom: "Bandcamp",
artworkUrl: resultItem.img.replace("/img/", "/img/a"),
joinFrom: "Bandcamp",
joinUrl: resultItem.item_url_path
}
}
}
},
"coverartarchive": async (metadata) => {
if ((metadata.MUSICBRAINZ_ALBUMID) && metadata.title)
{
const result = await axios.get("https://coverartarchive.org/release/" + metadata.MUSICBRAINZ_ALBUMID, {
headers: {"Accept-Encoding": "gzip,deflate,compress" }})
if (result.data.images[0] !== undefined)
{
console.warn(result.data.images[0].thumbnails.small);
return {
artworkFrom: "Cover Art Archive",
artworkUrl: result.data.images[0].thumbnails.small,
joinFrom: "Cover Art Archive",
joinUrl: result.data.release
};
}
}
},
"deezer": async (metadata) => {
//return musichoardersFetcher.fetch("deezer", metadata);
},
"qobuz": async (metadata) => {
//return musichoardersFetcher.fetch("qobuz", metadata);
// Also seems to require access token
},
"spotify": async (metadata) => {
/* // Fails, no access token
console.log("Searching Spotify:");
const result = await axios.get("https://api-partner.spotify.com/pathfinder/v1/query", {
params: {
operationName: "searchDesktop",
variables: JSON.stringify({
"searchTerm": `${metadata.ALBUMARTIST || metadata.artist} ${metadata.title}`,
"offset":0,"limit":10,"numberOfTopResults":5,"includeAudiobooks":true
}),
extensions: JSON.stringify({
"persistedQuery": {
"version":1,
"sha256Hash":"130115162add6f3499d2f88ead8a37a7cad1d4d2314f3a206377035e7d26b74c"
}
})
},
headers: {"Accept-Encoding": "gzip,deflate,compress" }
});
console.log(result.data);
"extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22130115162add6f3499d2f88ead8a37a7cad1d4d2314f3a206377035e7d26b74c%22%7D%7D"
*/
},
"soundcloud": async (metadata) => {
//return musichoardersFetcher.fetch("soundcloud", metadata);
// Also seems to require access token
},
"tidal": async (metadata) => {
//return musichoardersFetcher.fetch("tidal", metadata);
}
"staticoverrides": (md) => staticOverridesFetcher.fetch(md),
"apple": (md) => appleFetcher.fetch(md),
"bandcamp": (md) => bandcampFetcher.fetch(md),
"coverartarchive": (md) => coverartarchiveFetcher.fetch(md)
}

/**
Expand Down Expand Up @@ -308,18 +208,14 @@ module.exports = async (status) => {
smallImageKey: status.state,
smallImageText: hoverTexts[1] || `Volume: ${Math.round(status.volume / 2.56)}%`,
instance: true,
buttons: (joinUrl && joinLabel
? [{
label: joinLabel,
url: joinUrl
}]
: undefined)
};

if (joinUrl && joinLabel)
{
output.buttons = [
{
label: joinLabel,
url: joinUrl
}
];
}

if(status.stats.decodedvideo > 0)
{ // if video
if (meta['YouTube Start Time'] !== undefined)
Expand Down

0 comments on commit 5c4755d

Please sign in to comment.