diff --git a/docs/changelog.md b/docs/changelog.md index 47823da..1e5c58e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,6 +8,7 @@ ## Версия 1.5.4 - Добавлена попытка повторного чтения/записи при неизвестной ошибки службы Google Диска. +- Добавлена функция: [Lastfm.rangeTags](/func?id=rangetags). - [addToQueue](/func?id=addtoqueue) может добавлять массив треков в очередь. - К [mineTracks](/func?id=minetracks) добавлен параметр для пропуска элементов. - К [removeUnavailable](/func?id=removeunavailable) добавлен аргумент отключения сообщений с логами. diff --git a/docs/func.md b/docs/func.md index b25836a..d4a201a 100644 --- a/docs/func.md +++ b/docs/func.md @@ -877,6 +877,43 @@ let tracks = Lastfm.getTopTracks({ }); ``` +### rangeTags + +Оставляет только треки, которые содержат указанные теги Lastfm. Треки непрошедшие проверку удаляются из оригинального массива `spotifyTracks`. К каждому треку добавляется ключ `tags`, содержащий массив тегов. + +!> На каждый трек тратится по одному запросу. Используйте функцию только после максимального сокращения массива другими фильтрами. + +Аргументы +- (массив) `spotifyTracks` - проверяемые Spotify-треки. +- (объект) `args` - условия проверки. + - (массив) `include` - массив с объектами тегов. Если есть у трека (хотя бы один), он сохраняется. + - (массив) `exclude` - массив с объектами тегов. Если есть у трека (хотя бы один), он удаляется. + - (бул) `isRemoveUnknown` - при `true` треки без тегов удаляются. При `false` остаются. По умолчанию `false`. + - (бул) `isLogging` - при `true` выводятся сообщения о треках, для которых нет тегов. При `false` не выводит. По умолчанию `false`. + +Пример 1 - Оставить из недавних любимых треков только рок, исключая инди. Так как теги проставляются пользователями, у них есть показатель популярности (до 100). `minCount` - минимальное значение показателя включительно. Если будет меньше, трек удаляется не смотря на наличие тега. +```js +let tracks = Source.getSavedTracks(20); +Lastfm.rangeTags(tracks, { + isLogging: true, + isRemoveUnknown: true, + include: [ + { name: 'rock', minCount: 10 }, + ], + exclude: [ + { name: 'indie', minCount: 10 }, + ] +}); +``` + +Пример 2 - Добавить к трекам теги, удалить неизвестные. +```js +let tracks = Source.getSavedTracks(20); +Lastfm.rangeTags(tracks, { + isRemoveUnknown: true, +}); +``` + ### removeRecentArtists Удаляет из массива треков `original` историю недавно прослушанных `limit` треков пользователя `lastfmUser`. Совпадение определяется только по имени исполнителя. Требуется [дополнительная настройка](/install?id=Настройка-lastfm). Из-за разного написания имен в Lastfm и Spotify удалятся не все треки. diff --git a/library.js b/library.js index 67ce378..a69f154 100644 --- a/library.js +++ b/library.js @@ -635,7 +635,7 @@ const Player = (function () { addItem(tracks.id); } - function addItem(id){ + function addItem(id) { let queryObj = { uri: `spotify:track:${id}` }; let url = createUrl('/me/player/queue', deviceId, queryObj); return SpotifyRequest.post(url); @@ -2073,6 +2073,7 @@ const Lastfm = (function () { const LASTFM_API_BASE_URL = 'http://ws.audioscrobbler.com/2.0/?'; const LASTFM_STATION = 'https://www.last.fm/player/station/user'; return { + rangeTags: rangeTags, removeRecentTracks: removeRecentTracks, removeRecentArtists: removeRecentArtists, getLovedTracks: getLovedTracks, @@ -2398,16 +2399,58 @@ const Lastfm = (function () { return CustomUrlFetchApp.fetch(url) || []; } + function isNowPlayling(track) { + return track['@attr'] && track['@attr'].nowplaying === 'true'; + } + + function rangeTags(spotifyTracks, args) { + spotifyTracks.forEach(t => { + let queryObj = { + method: 'track.gettoptags', + user: args.user || KeyValue.LASTFM_LOGIN, + artist: t.artists[0].name.formatName(), + track: t.name.formatName(), + autocorrect: 1, + }; + let response = CustomUrlFetchApp.fetch(createUrl(queryObj)); + let trackname = `${t.artists[0].name} - ${t.name}`; + if (response.toptags && response.toptags.tag) { + if (response.toptags.tag.length > 0) { + t.tags = response.toptags.tag + } else if (response.toptags.tag.length == 0 && args.isLogging) { + console.info('У трека нет тегов', trackname); + } + } else if (response.error && args.isLogging) { + console.info(`${response.message}. Трек: ${trackname}`); + } + }); + + Combiner.replace(spotifyTracks, spotifyTracks.filter(t => { + if (!t.hasOwnProperty('tags') || t.tags.length == 0) { + return !args.isRemoveUnknown; + } + return isSomeBelong(t, args.include, true) && !isSomeBelong(t, args.exclude, false); + })); + + function isSomeBelong(track, inputTags, defaultResult) { + if (!track || !Array.isArray(inputTags)) { + return defaultResult; + } + return inputTags.some(inputTag => { + return track.tags.some(trackTag => + trackTag.name == inputTag.name + && trackTag.count >= (inputTag.minCount || 0) + ); + }); + } + } + function createUrl(queryObj) { queryObj.api_key = KeyValue.LASTFM_API_KEY; queryObj.format = 'json'; return LASTFM_API_BASE_URL + CustomUrlFetchApp.parseQuery(queryObj); } - function isNowPlayling(track) { - return track['@attr'] && track['@attr'].nowplaying === 'true'; - } - function formatLastfmTrackKey(item) { let artist = item.artist.name ? item.artist.name : item.artist['#text']; return `${artist} ${item.name}`.formatName(); @@ -3254,7 +3297,7 @@ const Admin = (function () { String.prototype.formatName = function () { return this.toLowerCase() - .replace(/[',?!@#$%^&*()+-./\\]/g, ' ') + .replace(/['`,?!@#$%^&*()+-./\\]/g, ' ') .replace(/\s{2,}/g, ' ') .replace(/ё/g, 'е') .trim();