Skip to content
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

Refactor to improve function readability #148

Merged
merged 6 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading