Skip to content

Commit

Permalink
notifications, tray icon + progress, remove video (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
moshfeu authored Oct 16, 2019
1 parent a9d8ae5 commit 3637470
Show file tree
Hide file tree
Showing 23 changed files with 324 additions and 126 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ Find your download: [https://github.com/moshfeu/y2mp3/releases/latest](https://g

## Change log

##### 2.3.0

- Allow to remove videos from the list

<img width="82" alt="Remove from the list (feature screenshot)" src="https://user-images.githubusercontent.com/3723951/66964159-7a4e2d00-f07e-11e9-8a78-1fc971b92183.png">


- Icon in the tray that shows the progress

<img width="267" alt="progress (feature screenshot)" src="https://user-images.githubusercontent.com/3723951/66964583-bdf56680-f07f-11e9-8a9e-1fd7e6394175.jpg" />

- Show notification when video downloaded

<img width="357" alt="notification (feature screenshot)" src="https://user-images.githubusercontent.com/3723951/66964794-50960580-f080-11e9-97bd-9d7382ea2470.png">

- Bug fixes

##### 2.2.0

- Attach the video's thumbnail as album art
Expand Down
Binary file added assets/tray.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/tray@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
139 changes: 70 additions & 69 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@ import {
shell,
BrowserWindow,
MenuItemConstructorOptions,
Tray,
ipcMain,
} from 'electron';

import { join } from 'path';
import * as isDev from 'electron-is-dev';
import { platform, homedir } from 'os';
import { existsSync } from 'fs';
import * as fixPath from 'fix-path';
fixPath();

enum EWindowEvents {
OPEN_ABOUT = 'open-about',
OPEN_PREFERENCES = 'open-preferences',
WINDOW_FOCUS = 'window-focus'
}

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.

enum eos {
MAC = 'darwin',
WINDOWS = 'win32',
};

fixPath();
let win: BrowserWindow;
let tray: Tray;

function getIconFile() {
switch (platform()) {
Expand Down Expand Up @@ -56,6 +56,8 @@ function createWindow() {
icon: join(__dirname, 'resources/icons', getIconFile())
});

createMenu();

// and load the index.html of the app.
win.loadFile('index.html');

Expand All @@ -72,53 +74,77 @@ function createWindow() {
}
}

// Emitted when the window is closed.
win.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null
win = null;
tray.destroy();
tray = null;
}).on('focus', () => {
win.webContents.send(EWindowEvents.WINDOW_FOCUS);
});

try {
tray = new Tray(join(app.getAppPath(), 'assets', 'tray.png'));
tray.on('click', () => win.focus());

ipcMain.on('tray', (_, message: string) => {
tray.setToolTip(message);
});
} catch (error) {
console.error(error);
}
}

app.on('ready', createWindow)

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

app.on('activate', () => {
if (win === null) {
createWindow()
}
})

function createMenu() {
// Create the Application's main menu
const template: MenuItemConstructorOptions[] = [{
label: "Application",
submenu: [{
label: "About y2mp3",
click: function () {
win.webContents.send(EWindowEvents.OPEN_ABOUT);
}
},
{
label: "Preferences",
accelerator: "CommandOrControl+,",
click: function () {
win.webContents.send(EWindowEvents.OPEN_PREFERENCES);
}
},
{
type: "separator"
},
{
label: "Toggle Developer Tools",
accelerator: "CommandOrControl+Option+J",
click: function () {
win.webContents.openDevTools()
}
},
{
type: "separator"
},
{
label: "Quit",
accelerator: "CommandOrControl+Q",
click: function () {
app.quit();
}
label: "Application",
submenu: [{
label: "About y2mp3",
click: function () {
win.webContents.send(EWindowEvents.OPEN_ABOUT);
}
]
},
{
label: "Preferences",
accelerator: "CommandOrControl+,",
click: function () {
win.webContents.send(EWindowEvents.OPEN_PREFERENCES);
}
},
{
type: "separator"
},
{
label: "Toggle Developer Tools",
accelerator: "CommandOrControl+Option+J",
click: function () {
win.webContents.openDevTools()
}
},
{
type: "separator"
},
{
label: "Quit",
accelerator: "CommandOrControl+Q",
click: function () {
app.quit();
}
}]
},
{
label: "Edit",
Expand Down Expand Up @@ -195,29 +221,4 @@ function createWindow() {
];

Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})

app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow()
}
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
}
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "y2mp3",
"appname": "y2mp3",
"productName": "y2mp3",
"version": "2.2.0",
"version": "2.3.0",
"main": "main.js",
"author": {
"name": "MosheF",
Expand Down Expand Up @@ -57,6 +57,7 @@
"ytdl-core": "^0.29.5"
},
"devDependencies": {
"@types/async": "^3.0.3",
"@types/electron-is-dev": "^0.3.0",
"@types/enzyme": "^3.1.15",
"@types/fluent-ffmpeg": "2.1.2",
Expand Down Expand Up @@ -94,7 +95,8 @@
"files": [
"index.html",
"main.js",
"resources/**"
"resources/**",
"assets/**"
],
"mac": {
"icon": "resources/icons/mac/icon.icns"
Expand Down
2 changes: 1 addition & 1 deletion src/components/button-progress/button-progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class ButtonProgress extends React.Component<IButtonProgressProps, IButto

componentDidUpdate(oldProps: IButtonProgressProps) {
const { progress } = this.props;
if (progress && oldProps.progress !== progress) {
if ((progress || progress === 0) && oldProps.progress !== progress) {
if (progress === this.progressDone) {
this.done();
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class Form extends React.Component<IFormProps, IFormState> {

return (
<div className={['search-wrapper', containerActive && 'active' || '', hasResult && '-has-result' || '', inProcess && '-in-process' || ''].join(' ')}>
<form onSubmit={this.onSubmit}>
<form className="search-form" onSubmit={this.onSubmit}>
<div className="input-holder">
<input className="search-input" type="url" placeholder="https://www.youtube.com/..." value={terms} onChange={e => this.setState({terms: e.target.value})} />
<button type="button" className="search-icon" onClick={this.searchClick} disabled={inProcess}>
Expand Down
34 changes: 20 additions & 14 deletions src/components/install-ffmpeg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import * as React from 'react';
import { installFfmpeg } from '../services/ffmpeg-installer';
import { ButtonProgress } from './button-progress';
import { setFfmpegPath } from '../services/api';
import { Progress } from 'ffbinaries';

interface IInstallFFMpegState {
downloadProgress: number;
}

interface IInstallFFMpegProps {
onDone: () => void;
onError: (error: string) => void;
}

export class InstallFFMpeg extends React.Component<IInstallFFMpegProps, IInstallFFMpegState> {
Expand All @@ -20,24 +22,28 @@ export class InstallFFMpeg extends React.Component<IInstallFFMpegProps, IInstall
}
}

updateDownloadProgress = (data) => {
updateDownloadProgress = (data: Progress) => {
this.setState({
downloadProgress: Math.floor(data.progress * 100)
}, () => {
if (this.state.downloadProgress === 100) {
setFfmpegPath();
setTimeout(() => {
this.props.onDone();
}, 2000);
}
downloadProgress: Math.max(1, Math.floor(data.progress * 100))
});
}

download = () => {
this.setState({
downloadProgress: 1
});
installFfmpeg(this.updateDownloadProgress);
download = async () => {
try {
this.setState({
downloadProgress: 1
});
await installFfmpeg(this.updateDownloadProgress);
setFfmpegPath();
setTimeout(() => {
this.props.onDone();
}, 2000);
} catch (error) {
this.props.onError(error);
this.setState({
downloadProgress: 0
});
}
}

render() {
Expand Down
16 changes: 11 additions & 5 deletions src/components/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as React from 'react';
import * as DOM from 'react-dom';
import store from '../mobx/store';
import { observer } from 'mobx-react';
import { download } from '../services/api';
import { download, search } from '../services/api';
import { IVideoEntity } from '../types';
import { Video } from './video';
import { Form } from './form';
Expand All @@ -13,12 +13,13 @@ import { InstallFFMpeg } from './install-ffmpeg';
import { AboutModal } from './about-modal';
import { PreferencesModal } from './preferences-modal/preferences-modal';
import { ElectronEventsListener } from './electron-events-listener';
import { closeModal } from '../services/modalsAndAlerts';
import { closeModal, showCustomError } from '../services/modalsAndAlerts';
import { Message } from 'semantic-ui-react';
import * as classNames from 'classnames';
import { settingsManager } from '../services/settings';
import { checkForUpdateAndNotify } from '../services/check-for-update';
import AppMenu from './menu';
import { clear as clearTrayTooltip } from '../services/tray-messanger';

@observer
class Main extends React.Component<{}, {}> {
Expand All @@ -36,26 +37,31 @@ class Main extends React.Component<{}, {}> {
}

async componentDidMount() {
clearTrayTooltip();
const { checkForUpdate } = settingsManager;
if (checkForUpdate) {
checkForUpdateAndNotify();
}
}

async onSearch(url: string) {
search(url);
}

public render() {
const { searchInProgress, videos, message } = store;
return (
<div className="main">
<AppMenu />
<Form
hasResult={!!videos.length}
onSubmit={store.search}
onSubmit={this.onSearch}
onClear={() => store.clearResult()}
inProcess={searchInProgress}
>
{videos.length ?
<div>
<ButtonProgress text="Download All" onClick={this.downloadAll} />
<ButtonProgress text={`Download All (${videos.length})`} onClick={this.downloadAll} />
<div className="videos">
{videos.map((video, i) => (
<Video
Expand All @@ -72,7 +78,7 @@ class Main extends React.Component<{}, {}> {
: ''}
</Form>
{
!store.isFFMpegInstalled && <InstallFFMpeg onDone={() => store.isFFMpegInstalled = true} />
!store.isFFMpegInstalled && <InstallFFMpeg onDone={() => store.isFFMpegInstalled = true} onError={showCustomError} />
}
<AboutModal open={store.isAboutOpen} onClose={closeModal} />
<PreferencesModal open={store.isPreferencesOpen} onClose={closeModal} />
Expand Down
Loading

0 comments on commit 3637470

Please sign in to comment.