From 6cda3877fbfe3909fb7a62b155d1fb2452539136 Mon Sep 17 00:00:00 2001 From: Wolfgang Gassler Date: Tue, 13 Aug 2024 13:19:22 +0200 Subject: [PATCH] redcircle exporter for stats --- redcircle/.gitignore | 3 + redcircle/README.md | 13 ++++ redcircle/env.sample | 1 + redcircle/exporter.js | 74 +++++++++++++++++++++++ redcircle/package-lock.json | 114 ++++++++++++++++++++++++++++++++++++ redcircle/package.json | 6 ++ 6 files changed, 211 insertions(+) create mode 100644 redcircle/.gitignore create mode 100644 redcircle/README.md create mode 100644 redcircle/env.sample create mode 100644 redcircle/exporter.js create mode 100644 redcircle/package-lock.json create mode 100644 redcircle/package.json diff --git a/redcircle/.gitignore b/redcircle/.gitignore new file mode 100644 index 0000000..bef1211 --- /dev/null +++ b/redcircle/.gitignore @@ -0,0 +1,3 @@ +.env +episode_stats* +node_modules \ No newline at end of file diff --git a/redcircle/README.md b/redcircle/README.md new file mode 100644 index 0000000..729a8ab --- /dev/null +++ b/redcircle/README.md @@ -0,0 +1,13 @@ +# Extract Stats from RedCircle + +## Install + +```bash +npm install +``` + +## Run + +- Get the bearer token from a valid RedCircle session (just look it up in an arbitrary api request) +- Store the bearer token in .env +- run the exporter to get all stats: `node exporter.js` diff --git a/redcircle/env.sample b/redcircle/env.sample new file mode 100644 index 0000000..d9f7046 --- /dev/null +++ b/redcircle/env.sample @@ -0,0 +1 @@ +BEARER_TOKEN=4789d55a-2463-460c-9899-e04247416c2c \ No newline at end of file diff --git a/redcircle/exporter.js b/redcircle/exporter.js new file mode 100644 index 0000000..e183722 --- /dev/null +++ b/redcircle/exporter.js @@ -0,0 +1,74 @@ +require('dotenv').config(); +const axios = require('axios'); +const fs = require('fs'); + +// Load bearer token from environment variable +const BEARER_TOKEN = process.env.BEARER_TOKEN; + +// Calculate current time in seconds and one year ago in seconds +const currentTime = Math.floor(Date.now() / 1000); + +// Define URLs +const statsUrl = `https://app.redcircle.com/api/stats/downloads?arbitraryTimeRange=1639090800%2C${currentTime}&bucketTerms=download.episodeUUID&interval=1y&isUnique=true&showUUID=0ecfdfd7-fda1-4c3d-9515-476727f9df5e&timezone=Europe%2FVienna`; +const episodesUrl = 'https://app.redcircle.com/api/shows/0ecfdfd7-fda1-4c3d-9515-476727f9df5e/episodes'; + +// Define headers +const headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0', + 'Accept': '*/*', + 'Accept-Language': 'en-GB,en;q=0.8,de;q=0.6,de-AT;q=0.4,nl;q=0.2', + 'Accept-Encoding': 'gzip, deflate, br, zstd', + 'Referer': 'https://app.redcircle.com/stats', + 'Authorization': `Bearer ${BEARER_TOKEN}` +}; + +// Fetch data from the given URL +async function fetchData(url) { + try { + const response = await axios.get(url, { headers }); + return response.data; + } catch (error) { + console.error(`Error fetching data from ${url}:`, error.message); + return null; + } +} + +// Combine data from episodes and download stats +async function processStatsAndEpisodes() { + const episodes = await fetchData(episodesUrl); + const downloadStats = await fetchData(statsUrl); + + if (!episodes || !downloadStats) { + console.error('Failed to fetch episodes or download stats.'); + return; + } + + // Map download counts by episode UUID + const downloadCountMap = downloadStats.reduce((map, stat) => { + if (map[stat.pathValues[1]] === undefined) { + map[stat.pathValues[1]] = 0; + } + map[stat.pathValues[1]] += stat.count; + return map; + }, {}); + + // Combine episodes with their download counts + const result = episodes.map(episode => { + return { + uuid: episode.uuid, + title: episode.title, + guid: episode.guid, + count: downloadCountMap[episode.uuid] || 0, + publishedAt: new Date(episode.publishedAt * 1000).toISOString(), + statsTill: new Date(currentTime * 1000).toISOString(), + }; + }); + + // Write the result to a JSON file + const nowFileString = (new Date()).toISOString().replace(/:/g, '-'); + fs.writeFileSync(`episode_stats_${nowFileString}.json`, JSON.stringify(result, null, 2)); + console.log(`Wrote episode stats to episode_stats_${nowFileString}.json`); +} + +// Execute the main function +processStatsAndEpisodes(); diff --git a/redcircle/package-lock.json b/redcircle/package-lock.json new file mode 100644 index 0000000..8f5cf1d --- /dev/null +++ b/redcircle/package-lock.json @@ -0,0 +1,114 @@ +{ + "name": "redcircle", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "axios": "^1.7.3", + "dotenv": "^16.4.5" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + } + } +} diff --git a/redcircle/package.json b/redcircle/package.json new file mode 100644 index 0000000..472c858 --- /dev/null +++ b/redcircle/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "axios": "^1.7.3", + "dotenv": "^16.4.5" + } +}