Skip to content

Commit

Permalink
feature: copy album art to clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyshankman committed Dec 30, 2023
1 parent 5f892d4 commit 09b3f1f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 13 deletions.
12 changes: 12 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
IpcMainEvent,
OpenDialogReturnValue,
clipboard,
nativeImage,
} from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
Expand Down Expand Up @@ -53,6 +54,7 @@ ipcMain.on('get-album-art', async (event, arg) => {
path.join(app.getPath('userData'), 'userConfig.json'),
) as StoreStructure;
userConfig.lastPlayedSong = filePath;

fs.writeFileSync(
path.join(app.getPath('userData'), 'userConfig.json'),
JSON.stringify(userConfig),
Expand Down Expand Up @@ -347,6 +349,16 @@ ipcMain.on('copy-to-clipboard', async (event, arg): Promise<any> => {
clipboard.writeText(arg.text);
});

ipcMain.on('copy-art-to-clipboard', async (event, arg): Promise<any> => {
const filePath = arg.song;
const metadata = await mm.parseFile(filePath);
if (metadata.common.picture?.[0].data) {
clipboard.writeImage(
nativeImage.createFromBuffer(metadata.common.picture?.[0].data),
);
}
});

if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
Expand Down
1 change: 1 addition & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type Channels =
| 'initialize'
| 'get-album-art'
| 'copy-to-clipboard'
| 'copy-art-to-clipboard'
| 'show-in-finder'
| 'song-imported';

Expand Down
38 changes: 28 additions & 10 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import LinearProgressBar from './components/LinearProgressBar';
import { StoreStructure, SongSkeletonStructure } from '../common/common';
import './App.scss';
import ReusableSongMenu from './components/ReusableSongMenu';
import AlbumArtMenu from './components/AlbumArtMenu';

/**
* @TODO this is a monolithic file and needs refactoring into smaller components
Expand Down Expand Up @@ -124,6 +125,7 @@ function MainDash() {
const [shuffle, setShuffle] = useState(false);
const [repeating, setRepeating] = useState(false);
const [initialScrollIndex, setInitialScrollIndex] = useState(0);
const [showAlbumArtMenu, setShowAlbumArtMenu] = useState(false);

const bufferToDataUrl = async (
buffer: Buffer,
Expand Down Expand Up @@ -708,15 +710,31 @@ function MainDash() {
</div>
)}
{currentSongDataURL && (
<img
src={currentSongDataURL}
alt="Album Art"
className="object-cover rounded-lg shadow-md max-w-[280px] w-1/3"
style={{
aspectRatio: '200/200',
objectFit: 'cover',
}}
/>
<>
<img
src={currentSongDataURL}
alt="Album Art"
className="album-art object-cover rounded-lg shadow-md max-w-[280px] w-1/3"
style={{
aspectRatio: '200/200',
objectFit: 'cover',
}}
onContextMenu={(e) => {
e.preventDefault();
setShowAlbumArtMenu(true);
}}
/>
{showAlbumArtMenu && currentSong && (
<AlbumArtMenu
open
anchorEl={document.querySelector('.album-art')}
onClose={() => {
setShowAlbumArtMenu(false);
}}
song={currentSong}
/>
)}
</>
)}

<Tooltip title="Import Library">
Expand Down Expand Up @@ -953,7 +971,7 @@ function MainDash() {
*/}
{songMenu && (
<ReusableSongMenu
open={!!songMenu}
open
anchorEl={songMenu?.anchorEl}
onClose={() => {
setSongMenu(undefined);
Expand Down
44 changes: 44 additions & 0 deletions src/renderer/components/AlbumArtMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';

type AlbumArtMenuProps = {
anchorEl: HTMLElement | null;
onClose: () => void;
song: string;
};

export default function AlbumArtMenu(props: AlbumArtMenuProps) {
const { anchorEl, onClose, song } = props;

const copyAlbumArt = () => {
window.electron.ipcRenderer.sendMessage('copy-art-to-clipboard', {
song,
});
onClose();
};

return (
<Menu
id="basic-menu"
anchorEl={anchorEl}
open
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
onClose={onClose}
anchorOrigin={{
vertical: 'center',
horizontal: 'center',
}}
>
<MenuItem
sx={{
fontSize: '11px',
}}
onClick={copyAlbumArt}
>
Copy Image
</MenuItem>
</Menu>
);
}
5 changes: 2 additions & 3 deletions src/renderer/components/ReusableSongMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import MenuItem from '@mui/material/MenuItem';
import { SongSkeletonStructure } from '../../common/common';

type SongMenuProps = {
open: boolean;
anchorEl: HTMLElement | null;
onClose: () => void;
song: string;
songInfo: SongSkeletonStructure;
};

export default function ReusableSongMenu(props: SongMenuProps) {
const { open, anchorEl, onClose, song, songInfo } = props;
const { anchorEl, onClose, song, songInfo } = props;

const showPathInFinder = () => {
window.electron.ipcRenderer.sendMessage('show-in-finder', {
Expand All @@ -31,7 +30,7 @@ export default function ReusableSongMenu(props: SongMenuProps) {
<Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
open
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
Expand Down

0 comments on commit 09b3f1f

Please sign in to comment.