Skip to content

Commit

Permalink
Refactor to improve function readability (#148)
Browse files Browse the repository at this point in the history
* Refactor

* Update learn-playlist-name.ts

* Update learn-playlist-name.ts

* Create spotify-seasonal-advanced.yml

* Adjust

* Build action

---------

Co-authored-by: GitHub Action <[email protected]>
  • Loading branch information
katydecorah and actions-user authored Jun 6, 2024
1 parent 92ab9bc commit 07660b3
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 56 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/spotify-seasonal.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
name: Save seasonal playlist
name: Save seasonal or any playlist

on:
# Run every three months on the 20th to get the seasonal playlist
schedule:
- cron: "00 01 20 Mar,Jun,Sep,Dec *"
# Run on demand to get any playlist
workflow_dispatch:
inputs:
playlist-name:
description: Your Spotify playlist name that you want to export.
required: true
type: string

permissions:
contents: write
Expand Down
88 changes: 64 additions & 24 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49338,28 +49338,51 @@ var github = __nccwpck_require__(5438);


function learnPlaylistName() {
const payload = github.context.payload.inputs;
// Extract the playlist name from the payload if it exists
const payload = github?.context?.payload?.inputs;
if (payload && payload["playlist-name"]) {
return payload["playlist-name"];
}
const today = new Date();
const month = process.env.MONTH
? parseInt(process.env.MONTH)
: today.getMonth();
const year = process.env.YEAR
? parseInt(process.env.YEAR)
: today.getFullYear();
return getSeasonalPlaylistName();
}
function getSeasonalPlaylistName() {
const { month, year } = getMonthYear();
const season = getSeason(month);
// Return the playlist name, which is the year and season name
// If the month is March, the year is the previous year and the current year
const playlistYear = month === 2 ? `${year - 1}/${year}` : year;
return `${playlistYear} ${season}`;
}
function getSeason(month) {
// Define the end of season names
const [marchEnd, juneEnd, septemberEnd, decemberEnd] = validateSeasonNames();
// Map the end of season months to their names
const seasons = {
2: marchEnd,
5: juneEnd,
8: septemberEnd,
11: decemberEnd,
};
// Get the season name for the current month
const season = seasons[month];
if (!season)
// Throw an error if the current month is not an end of season month
if (!season) {
throw new Error(`The current month does not match an end of season month.`);
return `${month === 2 ? `${year - 1}/${year}` : year} ${season}`;
}
return season;
}
function getMonthYear() {
const today = new Date();
const month = process.env.MONTH
? parseInt(process.env.MONTH)
: today.getMonth();
const year = process.env.YEAR
? parseInt(process.env.YEAR)
: today.getFullYear();
return {
month,
year,
};
}
function validateSeasonNames() {
const seasonNames = (0,lib_core.getInput)("season-names")
Expand All @@ -49378,21 +49401,10 @@ var server_default = /*#__PURE__*/__nccwpck_require__.n(server);

async function listPlaylists(listName) {
try {
const spotifyApi = new (server_default())({
clientId: process.env.SpotifyClientID,
clientSecret: process.env.SpotifyClientSecret,
});
const spotifyApi = await initializeSpotifyApi();
const username = (0,lib_core.getInput)("spotify-username");
const { body: { access_token }, } = await spotifyApi.clientCredentialsGrant();
spotifyApi.setAccessToken(access_token);
const { body } = await spotifyApi.getUserPlaylists(username);
const findPlaylist = body.items.find(({ name }) => name === listName);
if (!findPlaylist) {
throw new Error(`Could not find playlist "${listName}". Is it private?`);
}
const { body: { items }, } = await spotifyApi.getPlaylistTracks(findPlaylist.id);
if (!items.length)
throw new Error("Playlist has no tracks.");
const findPlaylist = await getPlaylist(spotifyApi, username, listName);
const items = await getPlaylistTracks(spotifyApi, findPlaylist.id);
return formatTracks({
name: findPlaylist.name,
external_urls: findPlaylist.external_urls,
Expand All @@ -49404,6 +49416,34 @@ async function listPlaylists(listName) {
throw new Error(error);
}
}
async function getPlaylist(spotifyApi, username, listName) {
const { body } = await spotifyApi.getUserPlaylists(username);
const findPlaylist = body.items.find(({ name }) => name === listName);
if (!findPlaylist) {
throw new Error(`Could not find playlist "${listName}". Is it private?`);
}
return findPlaylist;
}
async function getPlaylistTracks(spotifyApi, playlistId) {
const { body: { items }, } = await spotifyApi.getPlaylistTracks(playlistId);
if (!items.length)
throw new Error("Playlist has no tracks.");
return items;
}
async function initializeSpotifyApi() {
const clientId = process.env.SpotifyClientID;
const clientSecret = process.env.SpotifyClientSecret;
if (!clientId || !clientSecret) {
throw new Error("Missing SpotifyClientID or SpotifyClientSecret in environment variables");
}
const spotifyApi = new (server_default())({
clientId,
clientSecret,
});
const { body: { access_token }, } = await spotifyApi.clientCredentialsGrant();
spotifyApi.setAccessToken(access_token);
return spotifyApi;
}
function formatTracks({ name, external_urls, images, tracks, }) {
const largestImage = images.sort((a, b) => (b.width || 0) - (a.width || 0))[0];
return {
Expand Down
3 changes: 3 additions & 0 deletions src/__test__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ describe("action", () => {
jest
.spyOn(core, "getInput")
.mockImplementation((name) => defaultInputs[name] || undefined);

process.env.SpotifyClientID = "test-client-id";
process.env.SpotifyClientSecret = "test-client-secret";
});

test("works", async () => {
Expand Down
14 changes: 14 additions & 0 deletions src/__test__/list-playlists.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jest.mock("spotify-web-api-node", () => {
jest.mock("@actions/core");

describe("listPlaylists", () => {
beforeEach(() => {
process.env.SpotifyClientID = "test-client-id";
process.env.SpotifyClientSecret = "test-client-secret";
jest.clearAllMocks();
});
test("returns", async () => {
expect(await listPlaylists("2021 Fall")).toMatchInlineSnapshot(`
{
Expand Down Expand Up @@ -177,6 +182,15 @@ describe("listPlaylists", () => {
'Could not find playlist "2022 Fall". Is it private?'
);
});

test("throw error when SpotifyClientID or SpotifyClientSecret is missing", async () => {
delete process.env.SpotifyClientID;
delete process.env.SpotifyClientSecret;

await expect(listPlaylists("2021 Fall")).rejects.toThrow(
"Missing SpotifyClientID or SpotifyClientSecret in environment variables"
);
});
});

describe("formatTracks", () => {
Expand Down
55 changes: 43 additions & 12 deletions src/learn-playlist-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,63 @@ import { getInput } from "@actions/core";
import * as github from "@actions/github";

export default function learnPlaylistName(): string {
const payload = github.context.payload.inputs;
// Extract the playlist name from the payload if it exists
const payload = github?.context?.payload?.inputs;
if (payload && payload["playlist-name"]) {
return payload["playlist-name"];
}

const today = new Date();
const month = process.env.MONTH
? parseInt(process.env.MONTH)
: today.getMonth();
const year = process.env.YEAR
? parseInt(process.env.YEAR)
: today.getFullYear();
return getSeasonalPlaylistName();
}

function getSeasonalPlaylistName(): string {
const { month, year } = getMonthYear();
const season = getSeason(month);
// Return the playlist name, which is the year and season name
// If the month is March, the year is the previous year and the current year
const playlistYear = month === 2 ? `${year - 1}/${year}` : year;
return `${playlistYear} ${season}`;
}

function getSeason(month: number): string {
// Define the end of season names
const [marchEnd, juneEnd, septemberEnd, decemberEnd] = validateSeasonNames();
const seasons = {

// Map the end of season months to their names
const seasons: Record<number, string> = {
2: marchEnd,
5: juneEnd,
8: septemberEnd,
11: decemberEnd,
};

// Get the season name for the current month
const season = seasons[month];
if (!season)

// Throw an error if the current month is not an end of season month
if (!season) {
throw new Error(`The current month does not match an end of season month.`);
return `${month === 2 ? `${year - 1}/${year}` : year} ${season}`;
}

return season;
}

function getMonthYear(): { month: number; year: number } {
const today = new Date();
const month = process.env.MONTH
? parseInt(process.env.MONTH)
: today.getMonth();
const year = process.env.YEAR
? parseInt(process.env.YEAR)
: today.getFullYear();

return {
month,
year,
};
}

function validateSeasonNames() {
function validateSeasonNames(): string[] {
const seasonNames = getInput("season-names")
.split(",")
.map((s) => s.trim());
Expand Down
75 changes: 56 additions & 19 deletions src/list-playlists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,11 @@ export default async function listPlaylists(
listName: string
): Promise<Playlist> {
try {
const spotifyApi = new SpotifyWebApi({
clientId: process.env.SpotifyClientID,
clientSecret: process.env.SpotifyClientSecret,
});
const spotifyApi = await initializeSpotifyApi();
const username = getInput("spotify-username");
const {
body: { access_token },
} = await spotifyApi.clientCredentialsGrant();

spotifyApi.setAccessToken(access_token);

const { body } = await spotifyApi.getUserPlaylists(username);
const findPlaylist = body.items.find(({ name }) => name === listName);
if (!findPlaylist) {
throw new Error(`Could not find playlist "${listName}". Is it private?`);
}
const {
body: { items },
} = await spotifyApi.getPlaylistTracks(findPlaylist.id);
if (!items.length) throw new Error("Playlist has no tracks.");

const findPlaylist = await getPlaylist(spotifyApi, username, listName);
const items = await getPlaylistTracks(spotifyApi, findPlaylist.id);

return formatTracks({
name: findPlaylist.name,
Expand All @@ -38,6 +23,58 @@ export default async function listPlaylists(
}
}

async function getPlaylist(
spotifyApi: SpotifyWebApi,
username: string,
listName: string
): Promise<SpotifyApi.PlaylistObjectSimplified> {
const { body } = await spotifyApi.getUserPlaylists(username);
const findPlaylist = body.items.find(({ name }) => name === listName);

if (!findPlaylist) {
throw new Error(`Could not find playlist "${listName}". Is it private?`);
}

return findPlaylist;
}

async function getPlaylistTracks(
spotifyApi: SpotifyWebApi,
playlistId: string
): Promise<SpotifyApi.PlaylistTrackObject[]> {
const {
body: { items },
} = await spotifyApi.getPlaylistTracks(playlistId);

if (!items.length) throw new Error("Playlist has no tracks.");

return items;
}

export async function initializeSpotifyApi(): Promise<SpotifyWebApi> {
const clientId = process.env.SpotifyClientID;
const clientSecret = process.env.SpotifyClientSecret;

if (!clientId || !clientSecret) {
throw new Error(
"Missing SpotifyClientID or SpotifyClientSecret in environment variables"
);
}

const spotifyApi = new SpotifyWebApi({
clientId,
clientSecret,
});

const {
body: { access_token },
} = await spotifyApi.clientCredentialsGrant();

spotifyApi.setAccessToken(access_token);

return spotifyApi;
}

export function formatTracks({
name,
external_urls,
Expand Down

0 comments on commit 07660b3

Please sign in to comment.