Skip to content

Commit

Permalink
Pagination & Refactor(v0.0.5) (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
teofanis authored Oct 13, 2022
1 parent 34849ca commit d074090
Show file tree
Hide file tree
Showing 114 changed files with 2,144 additions and 538 deletions.
11 changes: 7 additions & 4 deletions .erb/scripts/jest-after-env.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import '@testing-library/jest-dom';

Object.assign(window, {
electron: {
ipcRenderer: {
on: jest.fn(),
sendMessage: jest.fn(),
removeAllListeners: jest.fn(),
store: {
getState: jest.fn(),
setState: jest.fn(),
subscribe: jest.fn(),
},
ipc: {
send: jest.fn(),
},
},
});
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:

steps:
- name: Checkout git repo
uses: actions/checkout@v1
uses: actions/checkout@v3

- name: Install Node and NPM
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: 16
cache: npm
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:

steps:
- name: Check out Git repository
uses: actions/checkout@v1
uses: actions/checkout@v3

- name: Install Node.js and NPM
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: 16
cache: npm
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@
"package-lock.json": true,
"*.{css,sass,scss}.d.ts": true
},
"cSpell.words": ["cyrb", "youtubedl"]
"cSpell.words": ["cyrb", "edata", "truncable", "youtubedl"]
}
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ In use by family members and close friends.
## Demo

![electronV004demo](https://user-images.githubusercontent.com/47872542/193476486-8f3b3db3-e99b-45ab-b802-fc82849542a2.gif)

## Pending Items / Improvements

- ~~Download multiple songs from TXT file~~
- ~~Pagination~~
- Expose More Options from ytbl (video, quality, etc..)
- Improve Design / UX
- Add a settings page
- Path to save downloads
- Dark Mode
- Search / Download ?
- One-click download chrome extension ?
- Search / Download / Browse page
- Refactor to follow best practises + write tests
- Multilingual

Expand Down
Binary file added misc/demo-gifs/v0.0.1.gif
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 misc/demo-gifs/v0.0.4.gif
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 misc/demo-gifs/v0.0.5.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,15 @@
},
"dependencies": {
"@headlessui/react": "^1.7.2",
"classnames": "^2.3.2",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.8",
"electron-shared-state": "^1.0.0",
"electron-updater": "^5.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"ytdl-core": "4.10.0",
"zustand": "^4.1.1"
"ytdl-core": "4.10.0"
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
Expand Down
12 changes: 6 additions & 6 deletions plopfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,30 @@ module.exports = function (plop) {
actions: [
{
type: 'add',
path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.tsx',
path: 'src/renderer/components/{{pascalCase name}}/{{pascalCase name}}.tsx',
templateFile: 'stubs/component.hbs',
},
{
type: 'add',
path: 'src/components/{{pascalCase name}}/index.ts',
path: 'src/renderer/components/{{pascalCase name}}/index.ts',
template:
"// eslint-disable-next-line import/prefer-default-export\nexport { default as {{pascalCase name}} } from './{{pascalCase name}}';\n",
},
{
type: 'add',
path: 'src/components/{{pascalCase name}}/__tests__/{{pascalCase name}}.test.tsx',
path: 'src/renderer/components/{{pascalCase name}}/__tests__/{{pascalCase name}}.test.tsx',
templateFile: 'stubs/component-test.hbs',
},
{
type: 'modify',
path: 'src/components/index.ts',
path: 'src/renderer/components/index.ts',
pattern: /(\/\/ Automatic import of components)/g,
template:
"import { {{pascalCase name}} } from 'components/{{pascalCase name}}';\n$1",
"import { {{pascalCase name}} } from 'renderer/components/{{pascalCase name}}';\n$1",
},
{
type: 'modify',
path: 'src/components/index.ts',
path: 'src/renderer/components/index.ts',
pattern: /(\/\/ Automatic export of components)/g,
template: '{{pascalCase name}},\n $1',
},
Expand Down
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": "ElectronYoutubeDownloader",
"version": "0.0.4",
"version": "0.0.5",
"description": "Electron Youtube Downloader",
"license": "MIT",
"author": {
Expand Down
179 changes: 179 additions & 0 deletions src/IPC/DownloaderChannel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/* eslint-disable global-require */
/* eslint-disable promise/always-return */
/* eslint-disable no-case-declarations */
/* eslint-disable import/no-named-as-default-member */
import path from 'path';
import { downloadMP3, getYoutubeLinkInfo } from '../libs/youtube-dl';
import { setState } from '../main/store';
import { errorResponse, successResponse, validateYoutubeLink } from '../utils';
/* eslint-disable class-methods-use-this */
import {
DownloadQueueItem,
IpcChannelInterface,
IpcRequest,
} from '../interfaces';
import { DownloaderActions } from '../main/actions/Downloader';
import {
DownloaderReducer,
updateDownloadItemStatus,
updateMetadata,
} from '../main/reducers/Downloader';

export default class DownloaderChannel implements IpcChannelInterface {
getName(): string {
return 'downloader-channel';
}

handleFileDialog(): {
status: string;
file?: string;
message?: string;
data?: string[];
error?: string;
} {
const { dialog } = require('electron');
const selectedFile = dialog.showOpenDialogSync({
properties: ['openFile'],
filters: [{ name: 'Text Files', extensions: ['txt'] }],
});
if (!selectedFile?.length) {
return {
status: 'error',
message: 'No file selected',
};
}
let reply = {
status: 'success',
file: path.basename(selectedFile[0]),
data: [],
};
try {
const fs = require('fs');
const data = fs.readFileSync(selectedFile[0], 'utf8');
const urls = data.trim().split('\n');
const validatedUrls = urls
.map((url: string) => url.trim())
.filter((url: string) => validateYoutubeLink(url));
reply = { ...reply, data: validatedUrls };
} catch (err) {
console.error(err);
reply = { ...reply, status: 'error' };
}
return reply;
}

handle(event: Electron.IpcMainEvent, request: IpcRequest): void {
if (!request.responseChannel) {
request.responseChannel = `${this.getName()}_response`;
}
const { responseChannel } = request;
const { action, payload } = request;
if (!action || !payload) return;
const {
START_DOWNLOAD,
CANCEL_DOWNLOAD,
ADD_TO_DOWNLOAD_QUEUE,
CLEAR_DOWNLOAD_QUEUE,
REMOVE_FROM_DOWNLOAD_QUEUE,
DOWNLOAD_FROM_TEXT_FILE,
LOAD_LINK_METADATA,
} = DownloaderActions;
switch (action) {
case ADD_TO_DOWNLOAD_QUEUE:
setState((state) =>
DownloaderReducer[ADD_TO_DOWNLOAD_QUEUE](
state,
payload as DownloadQueueItem
)
);
break;
case REMOVE_FROM_DOWNLOAD_QUEUE:
setState((state) =>
DownloaderReducer[REMOVE_FROM_DOWNLOAD_QUEUE](
state,
payload as DownloadQueueItem
)
);
break;
case START_DOWNLOAD:
const item = payload as DownloadQueueItem;
setState((state) => DownloaderReducer[START_DOWNLOAD](state, item));
downloadMP3(item.url)
.then((response) => {
if (response.status === 'success') {
updateDownloadItemStatus(item, 'downloaded');
event.sender.send(
responseChannel,
successResponse(this, 'Downloading', response)
);
} else {
event.sender.send(
responseChannel,
errorResponse(this, 'Failed to start download')
);
}
})
.catch((error) => {
updateDownloadItemStatus(item, 'error');
event.sender.send(
responseChannel,
errorResponse(this, 'Failed to start download')
);
console.log(error);
});
break;
case CANCEL_DOWNLOAD:
setState((state) =>
DownloaderReducer[CANCEL_DOWNLOAD](
state,
payload as DownloadQueueItem
)
);
break;
case CLEAR_DOWNLOAD_QUEUE:
setState((state) =>
DownloaderReducer[CLEAR_DOWNLOAD_QUEUE](
state,
{} as DownloadQueueItem
)
);
break;
case DOWNLOAD_FROM_TEXT_FILE:
const { status, file, data } = this.handleFileDialog();
if (status === 'success' && data) {
event.sender.send(
responseChannel,
successResponse(this, 'File selected', { file, data })
);
} else {
event.sender.send(
responseChannel,
errorResponse(this, 'Failed to start download')
);
}
break;
case LOAD_LINK_METADATA:
const { url } = payload as DownloadQueueItem;
getYoutubeLinkInfo(url)
.then((response) => {
const { videoDetails } = response;
updateMetadata(url, videoDetails);
event.sender.send(
responseChannel,
successResponse(this, 'Info Retrieved', videoDetails)
);
})
.catch((error) => {
console.log(error);
event.sender.send(
responseChannel,
errorResponse(this, 'Failed to get youtube link info')
);
});
break;
default:
break;
}
console.log(request);
}
}
21 changes: 21 additions & 0 deletions src/IPC/IpcResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
IpcResponse as IpcResponseI,
IpcResponseArgs,
IpcResponseBody,
} from '../interfaces';

export default class IpcResponse implements IpcResponseI {
responder: string;

response: IpcResponseBody;

constructor(args: IpcResponseArgs) {
const { ipcChannel, status, message, data } = args;
this.responder = ipcChannel.getName();
this.response = {
status,
message,
data,
};
}
}
31 changes: 31 additions & 0 deletions src/IPC/IpcService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* eslint-disable global-require */
import { IpcRenderer } from 'electron';
import { IpcRequest, PromisedIpcResponse } from 'interfaces';

export default class IpcService {
ipcRenderer?: IpcRenderer;

public constructor(ipcRenderer: IpcRenderer) {
this.ipcRenderer = ipcRenderer;
}

public send<T>(channel: string, request: IpcRequest): PromisedIpcResponse<T> {
if (!this.ipcRenderer) {
throw new Error(`Unable to require renderer process`);
}
const responseChannel = request?.responseChannel ?? `${channel}_response`;
const { ipcRenderer } = this;

if (!ipcRenderer) {
throw new Error(`Unable to send message to channel ${channel}`);
}

ipcRenderer.send(channel, request);

return new Promise((resolve) => {
return ipcRenderer.once(responseChannel, (_event, response) =>
resolve(response)
);
});
}
}
2 changes: 2 additions & 0 deletions src/IPC/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as DownloaderChannel } from './DownloaderChannel';
export { default as IpcService } from './IpcService';
Loading

0 comments on commit d074090

Please sign in to comment.