Skip to content

Conversation

aryash45
Copy link

@aryash45 aryash45 commented Oct 5, 2025

Title: feat(plugins): Add Playlist Exporter
Description:
This pull request introduces a new plugin, Playlist Exporter, which allows users to export their current song queue to a CSV file.

Motivation
Currently, there is no easy way to get a structured list of songs from a playlist or queue out of the application. This plugin solves that problem by providing a simple, one-click solution to export the current queue's metadata (including title, artist, album, duration, and video ID) into a universally compatible CSV format.

This addresses the user need to manage, archive, or share playlist data without cumbersome manual effort, as requested in issue #[issue number].

Implementation
Adds a new plugin in src/plugins/playlist-exporter.

The plugin was built following the official guide in the README.md, with separate backend.ts and renderer.ts files for a clean separation of concerns.

An "Export Current Queue to CSV" option is added to the main application menu.

Uses Electron's dialog for a native "Save File" experience.

How to Test
Enable the "Playlist Exporter" plugin in the options.

Play any song to populate the song queue.

From the top menu, select "Export Current Queue to CSV".

A save dialog should appear. Choose a location to save the playlist.csv file.

Open the saved file and verify that the song data is accurate and correctly formatted.

Resolves #3972

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

eslint

🚫 [eslint] <@typescript-eslint/no-explicit-any> reported by reviewdog 🐶
Unexpected any. Specify a different type.

const playlistData = queue.map((song: any) => ({


🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.

title: song.videoDetails?.title,


🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .videoDetails on an any value.

title: song.videoDetails?.title,


🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.

artist: song.videoDetails?.author,


🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .videoDetails on an any value.

artist: song.videoDetails?.author,


🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.

album: song.videoDetails?.album,


🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .videoDetails on an any value.

album: song.videoDetails?.album,


🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.

durationSeconds: song.videoDetails?.lengthSeconds,


🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .videoDetails on an any value.

durationSeconds: song.videoDetails?.lengthSeconds,


🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.

videoId: song.videoDetails?.videoId,


🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .videoDetails on an any value.

videoId: song.videoDetails?.videoId,


🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Insert

aryash45 and others added 2 commits October 5, 2025 22:42
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

import type { BackendContext } from '../../types/contexts';

const convertToCSV = (data: any[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-explicit-any> reported by reviewdog 🐶
Unexpected any. Specify a different type.


const convertToCSV = (data: any[]) => {
if (data.length === 0) return '';
const header = Object.keys(data[0]).join(',');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-argument> reported by reviewdog 🐶
Unsafe argument of type any assigned to a parameter of type {}.

const convertToCSV = (data: any[]) => {
if (data.length === 0) return '';
const header = Object.keys(data[0]).join(',');
const rows = data.map(row =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Replace row with (row)

Suggested change
const rows = data.map(row =>
const rows = data.map((row) =>

const convertToCSV = (data: any[]) => {
if (data.length === 0) return '';
const header = Object.keys(data[0]).join(',');
const rows = data.map(row =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <stylistic/arrow-parens> reported by reviewdog 🐶
Expected parentheses around arrow function argument.

Suggested change
const rows = data.map(row =>
const rows = data.map((row) =>

if (data.length === 0) return '';
const header = Object.keys(data[0]).join(',');
const rows = data.map(row =>
Object.values(row).map(value => `"${String(value).replace(/"/g, '""')}"`).join(',')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-argument> reported by reviewdog 🐶
Unsafe argument of type any assigned to a parameter of type ArrayLike<unknown> | { [s: string]: unknown; }.

if (data.length === 0) return '';
const header = Object.keys(data[0]).join(',');
const rows = data.map(row =>
Object.values(row).map(value => `"${String(value).replace(/"/g, '""')}"`).join(',')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Replace .map(value·=>·"${String(value).replace(/"/g,·'""')}").join(',') with ⏎······.map((value)·=>·"${String(value).replace(/"/g,·'""')}")⏎······.join(','),

Suggested change
Object.values(row).map(value => `"${String(value).replace(/"/g, '""')}"`).join(',')
Object.values(row)
.map((value) => `"${String(value).replace(/"/g, '""')}"`)
.join(','),

if (data.length === 0) return '';
const header = Object.keys(data[0]).join(',');
const rows = data.map(row =>
Object.values(row).map(value => `"${String(value).replace(/"/g, '""')}"`).join(',')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <stylistic/arrow-parens> reported by reviewdog 🐶
Expected parentheses around arrow function argument.

Suggested change
Object.values(row).map(value => `"${String(value).replace(/"/g, '""')}"`).join(',')
Object.values(row).map((value) => `"${String(value).replace(/"/g, '""')}"`).join(',')


// Add the config type to BackendContext
export const backend = ({ ipc }: BackendContext<{ enabled: true }>) => {
ipc.on('save-playlist-data', async (_: any, data: any[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-explicit-any> reported by reviewdog 🐶
Unexpected any. Specify a different type.


// Add the config type to BackendContext
export const backend = ({ ipc }: BackendContext<{ enabled: true }>) => {
ipc.on('save-playlist-data', async (_: any, data: any[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-explicit-any> reported by reviewdog 🐶
Unexpected any. Specify a different type.

await fs.writeFile(filePath, csvData);
}
});
}; No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Insert

Suggested change
};
};

aryash45 and others added 2 commits October 5, 2025 22:44
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Comment on lines +1 to +4
import { createPlugin } from '../../utils';

import { backend } from './backend';
import { renderer } from './renderer';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <importPlugin/order> reported by reviewdog 🐶
../../utils import should occur after import of ./renderer

Suggested change
import { createPlugin } from '../../utils';
import { backend } from './backend';
import { renderer } from './renderer';
import { backend } from './backend';
import { renderer } from './renderer';
import { createPlugin } from '../../utils';

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@@ -0,0 +1,35 @@
import { promises as fs } from 'fs';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <importPlugin/order> reported by reviewdog 🐶
There should be at least one empty line between import groups

Suggested change
import { promises as fs } from 'fs';
import { promises as fs } from 'fs';

Comment on lines +4 to +5

import type { BackendContext } from '../../types/contexts';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Delete

Suggested change
import type { BackendContext } from '../../types/contexts';
import type { BackendContext } from '../../types/contexts';

aryash45 and others added 3 commits October 5, 2025 23:07
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Export playlist metadata

1 participant