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

Feature: Gapless Playback with Gapless-5 #41

Merged
merged 62 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
755b2bf
slowly but surely
johnnyshankman Nov 11, 2024
a2456c5
wip
johnnyshankman Nov 11, 2024
9762240
hm
johnnyshankman Nov 11, 2024
a19a805
works ish
johnnyshankman Nov 12, 2024
36fc73a
decent
johnnyshankman Nov 12, 2024
b116df1
clean
johnnyshankman Nov 12, 2024
300ac24
right comment
johnnyshankman Nov 12, 2024
792ef29
tighten
johnnyshankman Nov 12, 2024
75cd3a7
work around media session and indefinite pause
johnnyshankman Nov 12, 2024
831894c
more
johnnyshankman Nov 12, 2024
b630152
bug fixing galore
johnnyshankman Nov 12, 2024
0ac6f93
reimplement repeating
johnnyshankman Nov 12, 2024
3d6449c
fix: snap to 0 visually on new song, respect the paused state on skip
johnnyshankman Nov 12, 2024
f0cff89
Fix: Skip song during repeat NOT respecting paused state
johnnyshankman Nov 13, 2024
5f50002
Clean up hook usage, set last played song automatically from store
johnnyshankman Nov 13, 2024
1ba8fc6
move play previous song into store, so much simpler
johnnyshankman Nov 13, 2024
8714f41
comment
johnnyshankman Nov 13, 2024
af9d398
Bugfix: Wrong size placeholder + not draggable
johnnyshankman Nov 13, 2024
b181f94
update readme
johnnyshankman Nov 13, 2024
18f200e
readme
johnnyshankman Nov 13, 2024
78bba50
comment
johnnyshankman Nov 13, 2024
5f84014
remove todo
johnnyshankman Nov 13, 2024
48574b0
clean
johnnyshankman Nov 13, 2024
7268027
remove warns
johnnyshankman Nov 13, 2024
5216302
control the audio element correctly to fix airpod issue + secondary i…
johnnyshankman Nov 13, 2024
d62b1f2
properly emulate Prev behavior of music players / cd players
johnnyshankman Nov 13, 2024
c2f7772
DRY up media sesh update
johnnyshankman Nov 13, 2024
7262691
match expected music player next/prev/autonext scroll behavior
johnnyshankman Nov 13, 2024
0117b59
comment
johnnyshankman Nov 13, 2024
55d0864
minor fix to the import ux
johnnyshankman Nov 13, 2024
dc515d1
fix time estimate code
johnnyshankman Nov 13, 2024
906e0d8
fix shuffle history bug when going back
johnnyshankman Nov 14, 2024
314cf1a
Rename song prog component, DRY & fix the onclick handlers w/ fallback
johnnyshankman Nov 14, 2024
fd613fd
reduce clutter with quiet mode in menu
johnnyshankman Nov 14, 2024
1e46cbf
change sm to 600px from 500px for volume slider's sake
johnnyshankman Nov 14, 2024
bcf6487
woops invert logic here
johnnyshankman Nov 14, 2024
daf0092
coalesce store, fix playcounts
johnnyshankman Nov 15, 2024
f6513c8
fix playcount tracking to be more specific
johnnyshankman Nov 15, 2024
654891e
lint
johnnyshankman Nov 15, 2024
afab65d
DRY
johnnyshankman Nov 15, 2024
3f34f35
clean
johnnyshankman Nov 15, 2024
089cfe4
remove
johnnyshankman Nov 15, 2024
37d07f6
fix/simplify: allow the tracklist to get large but loadLimit 3
johnnyshankman Nov 16, 2024
a2ebeae
lint
johnnyshankman Nov 16, 2024
9f5f3fa
Bug fixes for shuffle, requires fixed version of gapless 5
johnnyshankman Nov 16, 2024
8915bab
install fixed version locally
johnnyshankman Nov 16, 2024
55a7c53
minor improvement to UX snappiness when changin position
johnnyshankman Nov 16, 2024
e98be68
comments
johnnyshankman Nov 16, 2024
e3762fb
comment
johnnyshankman Nov 16, 2024
e1bcf63
avoid state action weaving in SongProgress and StaticPlayer
johnnyshankman Nov 16, 2024
148fc42
reduce cross fade to avoid noticeable rhythmic issues
johnnyshankman Nov 17, 2024
7f53c9b
give quiet down an accelerator
johnnyshankman Nov 18, 2024
9bb062a
Feature: Advanced menu + volume controls from Accelerators
johnnyshankman Nov 18, 2024
f0f11aa
HELL YEAH: reimplement seeking from the media session
johnnyshankman Nov 18, 2024
9be3efc
comment
johnnyshankman Nov 18, 2024
0742bcf
Merge branch 'main' into johnny/gapless-5
johnnyshankman Nov 18, 2024
8a8d022
robustly handle the mesida session and position state
johnnyshankman Nov 18, 2024
ad282be
Never allow shuffle history bigger than 100
johnnyshankman Nov 18, 2024
88ed4fe
Minor bump to 1.1.0
johnnyshankman Nov 18, 2024
83dd1b3
simplify filtering
johnnyshankman Nov 19, 2024
a1ceafe
update with minor bug fix
johnnyshankman Nov 20, 2024
94f45c5
unsure what i did wrong there
johnnyshankman Nov 21, 2024
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
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ module.exports = {
],
// Add these new rules
'react/jsx-sort-props': ['error'],
// allow console.error and console.warn
'no-console': ['error', { allow: ['error', 'warn'] }],
},
parserOptions: {
ecmaVersion: 2022,
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ This project was built with the following technologies:
* [![zustand][zustand.js]][zustand-url]
* [![Tailwind][Tailwind.js]][Tailwind-url]
* [![Music Metadata][MusicMetadata.js]][MusicMetadata-url]
* [![Gapless 5][Gapless5.js]][Gapless5-url]

<p align="right">(<a href="#readme-top">back to top</a>)</p>

Expand Down Expand Up @@ -278,7 +279,7 @@ This project was built with the following technologies:
- [x] Show stats about your library somewhere, like GB and # of songs
- [x] iTunes-like browser with list of artist and album filters
- [x] Quiet mode creating bg music to a video/audiobook/podcast in another app
- [ ] True gapless playback
- [x] True gapless playback
- [ ] Edit song metadata
- [ ] Hide/show columns in the Library List
- [ ] Queue a next-up song
Expand Down Expand Up @@ -399,6 +400,8 @@ Project Link: [https://github.com/johnnyshankman/hihat](https://github.com/johnn
[Typescript-url]: https://typescriptlang.org
[zustand.js]: https://img.shields.io/badge/Zustand-20232A?style=for-the-badge&logo=javascript&logoColor=007ACC
[zustand-url]: [https://typescriptlang.org](https://github.com/pmndrs/zustand)https://github.com/pmndrs/zustand
[Gapless5.js]: https://img.shields.io/badge/Gapless5-20232A?style=for-the-badge&logo=javascript&logoColor=007ACC
[Gapless5-url]: https://github.com/regosen/Gapless-5



6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
}
},
"dependencies": {
"@regosen/gapless-5": "git+https://github.com/johnnyshankman/Gapless-5.git",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.8",
"electron-updater": "^6.1.4",
Expand Down
Binary file added regosen-gapless-5-1.5.6.tgz
Binary file not shown.
4 changes: 2 additions & 2 deletions release/app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion release/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hihat",
"version": "1.0.0",
"version": "1.1.0",
"description": "A minimalist offline music player for OSX audiophiles :: Based on iTunes circa 2002 :: by White Lights",
"license": "MIT",
"author": {
Expand Down
13 changes: 11 additions & 2 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,6 @@ ipcMain.on('set-last-played-song', async (event, arg: string) => {
Object.keys(userConfig.library).forEach((key) => {
if (key === songFilePath) {
userConfig.library[key].additionalInfo.lastPlayed = Date.now();
userConfig.library[key].additionalInfo.playCount += 1;
}
});

Expand All @@ -471,6 +470,16 @@ ipcMain.on('set-last-played-song', async (event, arg: string) => {
});
});

/**
* @dev increments the play count of the requested song
* @note expected the store will manually add 1 to the internal play count until reboot
*/
ipcMain.on('increment-play-count', async (event, arg: { song: string }) => {
const userConfig = getUserConfig();
userConfig.library[arg.song].additionalInfo.playCount += 1;
writeFileSyncToUserConfig(userConfig);
});

/**
* @dev for requesting the modification of a tag of a media file
* and then modifying it in the app as well as cache'ing it
Expand Down Expand Up @@ -947,7 +956,7 @@ const createWindow = async () => {
});

/**
* @importnat Set the global asset path to /assets
* @important Set the global asset path to /assets
*/
const RESOURCES_PATH = app.isPackaged
? path.join(process.resourcesPath, 'assets')
Expand Down
79 changes: 56 additions & 23 deletions src/main/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export default class MenuBuilder {
}

buildDarwinTemplate(): MenuItemConstructorOptions[] {
const subMenuAbout: DarwinMenuItemConstructorOptions = {
const subMenuHihat: DarwinMenuItemConstructorOptions = {
label: 'hihat',
submenu: [
{
Expand All @@ -121,38 +121,30 @@ export default class MenuBuilder {
this.mainWindow.webContents.send('menu-rescan-library');
},
},
{ type: 'separator' },
{
label: 'select library folder',
click: () => {
this.mainWindow.webContents.send('menu-select-library');
},
},
{
label: 'hide duplicate songs',
label: 'max volume',
accelerator: 'Command+Up',
click: () => {
this.mainWindow.webContents.send('menu-hide-dupes');
this.mainWindow.webContents.send('menu-max-volume');
},
},
{
label: 'delete duplicate songs',
label: 'mute volume',
accelerator: 'Command+Down',
click: () => {
this.mainWindow.webContents.send('menu-delete-dupes');
this.mainWindow.webContents.send('menu-mute-volume');
},
},
{
label: 'backup / sync library',
label: 'quiet',
accelerator: 'Option+Command+Down',
click: () => {
this.mainWindow.webContents.send('menu-backup-library');
this.mainWindow.webContents.send('menu-quiet-mode');
},
},
// {
// label: 'reset all hihat data',
// click: () => {
// this.mainWindow.webContents.send('menu-reset-library');
// },
// },
{ type: 'separator' },

{ type: 'separator' },
{
label: 'see library stats',
click: () => {
Expand All @@ -166,9 +158,7 @@ export default class MenuBuilder {
});
},
},

{ type: 'separator' },

{
label: 'hide hihat',
accelerator: 'Command+H',
Expand Down Expand Up @@ -238,6 +228,42 @@ export default class MenuBuilder {
this.mainWindow.webContents.send('menu-toggle-browser');
},
},
{
label: 'reset all hihat data',
click: () => {
this.mainWindow.webContents.send('menu-reset-library');
},
},
],
};
const subMenuAdvanced: DarwinMenuItemConstructorOptions = {
label: 'Advanced',
submenu: [
{
label: 'change library folder',
click: () => {
this.mainWindow.webContents.send('menu-select-library');
},
},
{
label: 'backup / sync library',
click: () => {
this.mainWindow.webContents.send('menu-backup-library');
},
},
{ type: 'separator' },
{
label: 'hide duplicate songs',
click: () => {
this.mainWindow.webContents.send('menu-hide-dupes');
},
},
{
label: 'delete duplicate songs',
click: () => {
this.mainWindow.webContents.send('menu-delete-dupes');
},
},
],
};
const subMenuViewProd: MenuItemConstructorOptions = {
Expand Down Expand Up @@ -299,7 +325,14 @@ export default class MenuBuilder {
? subMenuViewDev
: subMenuViewProd;

return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
return [
subMenuHihat,
subMenuEdit,
subMenuView,
subMenuAdvanced,
subMenuWindow,
subMenuHelp,
];
}

buildDefaultTemplate() {
Expand Down
10 changes: 10 additions & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ export type Channels =
| 'menu-reset-library'
| 'menu-hide-dupes'
| 'menu-delete-dupes'
| 'menu-max-volume'
| 'menu-quiet-mode'
| 'menu-mute-volume'
| 'song-imported'
| 'hide-song'
| 'delete-song'
| 'delete-album'
| 'menu-toggle-browser'
| 'increment-play-count'
| 'update-store';

export type ArgsBase = Record<Channels, unknown>;
Expand Down Expand Up @@ -64,6 +68,9 @@ export interface SendMessageArgs extends ArgsBase {
'show-in-finder': {
path: string;
};
'increment-play-count': {
song: string;
};
}

export interface ResponseArgs extends ArgsBase {
Expand All @@ -84,6 +91,9 @@ export interface ResponseArgs extends ArgsBase {
scrollToIndex?: number;
};
'backup-library-success': undefined;
'menu-quiet-mode': undefined;
'menu-max-volume': undefined;
'menu-mute-volume': undefined;
}

const electronHandler = {
Expand Down
46 changes: 38 additions & 8 deletions src/renderer/components/AlbumArt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import Draggable from 'react-draggable';
import { TinyText } from './SimpleStyledMaterialUIComponents';
import AlbumArtRightClickMenu from './AlbumArtRightClickMenu';
import usePlayerStore from '../store/player';
import useMainStore from '../store/main';
import { useWindowDimensions } from '../hooks/useWindowDimensions';

interface AlbumArtProps {
Expand All @@ -14,20 +14,23 @@ export default function AlbumArt({
setShowAlbumArtMenu,
showAlbumArtMenu,
}: AlbumArtProps) {
/**
* @dev window provider hook
*/
const { width, height } = useWindowDimensions();

/**
* @dev global store hooks
* @dev store hooks
*/
const currentSong = usePlayerStore((store) => store.currentSong);
const currentSongMetadata = usePlayerStore(
const currentSong = useMainStore((store) => store.currentSong);
const currentSongMetadata = useMainStore(
(store) => store.currentSongMetadata,
);
const currentSongDataURL = usePlayerStore(
const currentSongDataURL = useMainStore(
(store) => store.currentSongArtworkDataURL,
);
const filteredLibrary = usePlayerStore((store) => store.filteredLibrary);
const setOverrideScrollToIndex = usePlayerStore(
const filteredLibrary = useMainStore((store) => store.filteredLibrary);
const setOverrideScrollToIndex = useMainStore(
(store) => store.setOverrideScrollToIndex,
);

Expand Down Expand Up @@ -60,7 +63,7 @@ export default function AlbumArt({
if (!currentSongDataURL) {
return (
<div
className="relative aspect-square w-[40%] bg-gradient-to-r from-neutral-800 via-neutral-700 to-neutral-600 border-2 border-neutral-700 shadow-2xl rounded-lg transition-all duration-200"
className="relative aspect-square w-full bg-gradient-to-r from-neutral-800 via-neutral-700 to-neutral-600 border-2 border-neutral-700 shadow-2xl rounded-lg"
style={{
maxWidth: `${albumArtMaxWidth}px`,
}}
Expand All @@ -84,6 +87,33 @@ export default function AlbumArt({
</svg>
<TinyText className="absolute bottom-3 left-3">hihat</TinyText>
</div>
<Draggable
axis="y"
onDrag={(e, data) => {
if (!width || !height) return;
const newMaxWidth = albumArtMaxWidth + data.deltaY;
const maxWidthBasedOnWidth = Math.min(320, width * 0.4);
const maxWidthBasedOnHeight = Math.min(320, height * 0.6);
const clampedMaxWidth = Math.max(
120,
Math.min(
newMaxWidth,
maxWidthBasedOnWidth,
maxWidthBasedOnHeight,
),
);
setAlbumArtMaxWidth(clampedMaxWidth);

/**
* @important dispatch an event to let all components know the width has changed
* @see LibraryList.tsx
*/
window.dispatchEvent(new Event('album-art-width-changed'));
}}
position={{ x: 0, y: 0 }}
>
<div className="w-full absolute bottom-0 h-[20px] bg-transparent cursor-ns-resize hover:cursor-ns-resize" />
</Draggable>
</div>
);
}
Expand Down
Loading
Loading