yt-grabber 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.eslintrc.json +29 -0
  2. package/.prettierrc +19 -0
  3. package/.vscode/extensions.json +7 -0
  4. package/.vscode/settings.json +23 -0
  5. package/.yarnrc.yml +13 -0
  6. package/LICENSE +21 -0
  7. package/README.md +11 -0
  8. package/package.json +115 -0
  9. package/public/index.html +20 -0
  10. package/public/screenshots/cutting.png +0 -0
  11. package/public/screenshots/downloading.png +0 -0
  12. package/public/screenshots/editing.png +0 -0
  13. package/public/screenshots/errors.png +0 -0
  14. package/public/screenshots/settings.png +0 -0
  15. package/public/screenshots/tracklist.png +0 -0
  16. package/src/@types/global.d.ts +7 -0
  17. package/src/@types/i18next-scanner-webpack.d.ts +1 -0
  18. package/src/@types/stylus.d.ts +4 -0
  19. package/src/@types/svg.d.ts +1 -0
  20. package/src/App.styl +24 -0
  21. package/src/App.tsx +31 -0
  22. package/src/bootstrap.tsx +30 -0
  23. package/src/common/CancellablePromise.ts +22 -0
  24. package/src/common/ComponentDisplayMode.ts +8 -0
  25. package/src/common/Delay.ts +3 -0
  26. package/src/common/FileSystem.ts +171 -0
  27. package/src/common/Helpers.ts +270 -0
  28. package/src/common/Mappings.ts +14 -0
  29. package/src/common/PuppeteerOptions.ts +45 -0
  30. package/src/common/Selectors.ts +21 -0
  31. package/src/common/Store.ts +108 -0
  32. package/src/common/Theme.ts +4 -0
  33. package/src/common/Youtube.ts +80 -0
  34. package/src/components/appBar/AppBar.styl +22 -0
  35. package/src/components/appBar/AppBar.tsx +73 -0
  36. package/src/components/directoryPicker/DirectoryPicker.tsx +44 -0
  37. package/src/components/fileField/FileField.styl +3 -0
  38. package/src/components/fileField/FileField.tsx +152 -0
  39. package/src/components/languagePicker/LanguagePicker.styl +38 -0
  40. package/src/components/languagePicker/LanguagePicker.tsx +145 -0
  41. package/src/components/logo/Logo.tsx +15 -0
  42. package/src/components/modals/DetailsModal.styl +9 -0
  43. package/src/components/modals/DetailsModal.tsx +85 -0
  44. package/src/components/numberField/NumberField.styl +13 -0
  45. package/src/components/numberField/NumberField.tsx +154 -0
  46. package/src/components/progress/Progress.styl +15 -0
  47. package/src/components/progress/Progress.tsx +18 -0
  48. package/src/components/splitButton/SplitButton.styl +0 -0
  49. package/src/components/splitButton/SplitButton.tsx +125 -0
  50. package/src/components/themePicker/ThemePicker.styl +19 -0
  51. package/src/components/themePicker/ThemePicker.tsx +65 -0
  52. package/src/components/themeSwitcher/ThemeSwitcher.styl +10 -0
  53. package/src/components/themeSwitcher/ThemeSwitcher.tsx +43 -0
  54. package/src/components/youtube/formatSelector/FormatSelector.styl +3 -0
  55. package/src/components/youtube/formatSelector/FormatSelector.tsx +202 -0
  56. package/src/components/youtube/inputPanel/InputPanel.styl +7 -0
  57. package/src/components/youtube/inputPanel/InputPanel.tsx +189 -0
  58. package/src/components/youtube/mediaInfoPanel/MediaInfoPanel.styl +80 -0
  59. package/src/components/youtube/mediaInfoPanel/MediaInfoPanel.tsx +113 -0
  60. package/src/components/youtube/trackList/TrackList.styl +64 -0
  61. package/src/components/youtube/trackList/TrackList.tsx +258 -0
  62. package/src/enums/DataResponse.ts +5 -0
  63. package/src/enums/Media.ts +16 -0
  64. package/src/enums/MediaFormat.ts +10 -0
  65. package/src/enums/MimeTypes.ts +14 -0
  66. package/src/hooks/useCancellablePromises.ts +25 -0
  67. package/src/hooks/useClickCounter.ts +24 -0
  68. package/src/hooks/useData.ts +61 -0
  69. package/src/hooks/useMultiClickHandler.ts +41 -0
  70. package/src/i18next.ts +33 -0
  71. package/src/index.ts +65 -0
  72. package/src/react/actions/Action.ts +3 -0
  73. package/src/react/actions/AppActions.ts +41 -0
  74. package/src/react/contexts/AppContext.tsx +51 -0
  75. package/src/react/contexts/AppThemeContext.tsx +38 -0
  76. package/src/react/contexts/DataContext copy.tsx +76 -0
  77. package/src/react/contexts/DataContext.tsx +41 -0
  78. package/src/react/hooks/useAppTheme.ts +14 -0
  79. package/src/react/reducers/AppReducer.tsx +45 -0
  80. package/src/react/reducers/Reducer.ts +7 -0
  81. package/src/react/states/AppState.ts +29 -0
  82. package/src/react/states/State.ts +29 -0
  83. package/src/renderer.tsx +13 -0
  84. package/src/resources/bin/yt-dlp.exe +0 -0
  85. package/src/resources/fonts/Baloo-Regular.ttf +0 -0
  86. package/src/resources/fonts/Lato-Black.ttf +0 -0
  87. package/src/resources/fonts/Lato-BlackItalic.ttf +0 -0
  88. package/src/resources/fonts/Lato-Bold.ttf +0 -0
  89. package/src/resources/fonts/Lato-BoldItalic.ttf +0 -0
  90. package/src/resources/fonts/Lato-Italic.ttf +0 -0
  91. package/src/resources/fonts/Lato-Light.ttf +0 -0
  92. package/src/resources/fonts/Lato-LightItalic.ttf +0 -0
  93. package/src/resources/fonts/Lato-Regular.ttf +0 -0
  94. package/src/resources/fonts/Lato-Thin.ttf +0 -0
  95. package/src/resources/fonts/Lato-ThinItalic.ttf +0 -0
  96. package/src/resources/fonts/Material-Icons.woff2 +0 -0
  97. package/src/resources/icons/favicon-16x16.png +0 -0
  98. package/src/resources/icons/favicon-32x32.png +0 -0
  99. package/src/resources/icons/favicon.ico +0 -0
  100. package/src/resources/icons/logo-shape.png +0 -0
  101. package/src/resources/icons/logo-shape.svg +59 -0
  102. package/src/resources/images/loading.svg +28 -0
  103. package/src/resources/images/logo.png +0 -0
  104. package/src/resources/locales/de-DE/flag.svg +1 -0
  105. package/src/resources/locales/de-DE/translation.json +44 -0
  106. package/src/resources/locales/en-GB/flag.svg +43 -0
  107. package/src/resources/locales/en-GB/translation.json +44 -0
  108. package/src/resources/locales/pl-PL/flag.svg +36 -0
  109. package/src/resources/locales/pl-PL/translation.json +44 -0
  110. package/src/styles/MaterialThemes.ts +331 -0
  111. package/src/styles/fonts.styl +71 -0
  112. package/src/styles/mixins.styl +22 -0
  113. package/src/tests/CompleteTracksMock.ts +17384 -0
  114. package/src/tests/MissingDetailsTracksMock.ts +7737 -0
  115. package/src/theme/ColorThemes.ts +190 -0
  116. package/src/theme/Colors.ts +92 -0
  117. package/src/theme/Shadows.ts +9 -0
  118. package/src/theme/Shape.ts +7 -0
  119. package/src/theme/Theme.ts +24 -0
  120. package/src/theme/Typography.ts +56 -0
  121. package/src/views/development/DevelopmentView.styl +22 -0
  122. package/src/views/development/DevelopmentView.tsx +57 -0
  123. package/src/views/home/HomeView.styl +60 -0
  124. package/src/views/home/HomeView.tsx +505 -0
  125. package/src/views/settings/SettingsView.styl +27 -0
  126. package/src/views/settings/SettingsView.tsx +255 -0
  127. package/tsconfig.json +20 -0
  128. package/webpack.config.ts +226 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true
5
+ },
6
+ "extends": ["eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", "prettier"],
7
+ "parser": "@typescript-eslint/parser",
8
+ "parserOptions": {
9
+ "ecmaVersion": "latest",
10
+ "sourceType": "module"
11
+ },
12
+ "settings": {
13
+ "react": {
14
+ "version": "detect"
15
+ }
16
+ },
17
+ "plugins": ["react", "@typescript-eslint"],
18
+ "rules": {
19
+ "indent": ["error", 4],
20
+ "linebreak-style": ["error", "windows"],
21
+ "quotes": ["error", "double"],
22
+ "semi": ["error", "always"],
23
+ "@typescript-eslint/no-var-requires": 0,
24
+ "@typescript-eslint/no-explicit-any": 0,
25
+ "@typescript-eslint/no-empty-interface": 0,
26
+ "react/prop-types": 0,
27
+ "react/display-name": 0
28
+ }
29
+ }
package/.prettierrc ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "trailingComma": "all",
3
+ "semi": true,
4
+ "tabWidth": 4,
5
+ "singleQuote": false,
6
+ "printWidth": 120,
7
+ "bracketSpacing": false,
8
+ "arrowParens": "always",
9
+ "tslintIntegration": true,
10
+ "eslintIntegration": true,
11
+ "overrides": [
12
+ {
13
+ "files": ["*.json", ".prettierrc"],
14
+ "options": {
15
+ "tabWidth": 4
16
+ }
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "recommendations": [
3
+ "arcanis.vscode-zipfs",
4
+ "dbaeumer.vscode-eslint",
5
+ "esbenp.prettier-vscode"
6
+ ]
7
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "files.exclude": {
3
+ "**/.git": true,
4
+ "**/.svn": true,
5
+ "**/.hg": true,
6
+ "**/CVS": true,
7
+ "**/.DS_Store": false,
8
+ "**/Thumbs.db": false,
9
+ "**/.yarn/*/**": true,
10
+ "**/node_modules": true,
11
+ "**/node_modules/*/**": true,
12
+ "node_modules/*/**": true
13
+ },
14
+ "explorerExclude.backup": {},
15
+ "CodeGPT.apiKey": "CodeGPT Plus",
16
+ "search.exclude": {
17
+ "**/.yarn": true,
18
+ "**/.pnp.*": true
19
+ },
20
+ "eslint.nodePath": ".yarn/sdks",
21
+ "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs",
22
+ "typescript.enablePromptUseWorkspaceTsdk": true,
23
+ }
package/.yarnrc.yml ADDED
@@ -0,0 +1,13 @@
1
+ compressionLevel: mixed
2
+
3
+ enableGlobalCache: false
4
+
5
+ nodeLinker: node-modules
6
+
7
+ packageExtensions:
8
+ "usehooks-ts@*":
9
+ dependencies:
10
+ "react": "^19.0.0"
11
+ peerDependencies:
12
+ "react": "^19.0.0"
13
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Marcin Karpiński
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ ## Install:
2
+ `yarn install`
3
+
4
+ ## Build:
5
+ `yarn build`
6
+
7
+ ## Start:
8
+ `yarn start`
9
+
10
+ ## Usage:
11
+ Download and run latest binary release.
package/package.json ADDED
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "yt-grabber",
3
+ "version": "1.0.0",
4
+ "description": "Youtube Grabber",
5
+ "main": "./dist/index.js",
6
+ "repository": {
7
+ "url": "https://github.com/karenpommeroy/yt-grabber.git"
8
+ },
9
+ "scripts": {
10
+ "clean": "rimraf dist",
11
+ "start": "concurrently \"cross-env NODE_ENV=development yarn run watch\" \"yarn run electron\"",
12
+ "build": "cross-env NODE_ENV=development && yarn clean && webpack --config webpack.config.ts",
13
+ "build:prod": "cross-env NODE_ENV=production && yarn clean && webpack --config webpack.config.ts",
14
+ "electron": "wait-on dist/index.js && cross-env NODE_ENV=production ELECTRON_DISABLE_SECURITY_WARNINGS=true electron .",
15
+ "watch": "webpack --watch --config webpack.config.ts",
16
+ "pack": "yarn build:prod && electron-builder --dir",
17
+ "dist": "yarn build:prod && electron-builder"
18
+ },
19
+ "build": {
20
+ "appId": "yt.grabber",
21
+ "files": [
22
+ "dist/**/*",
23
+ "package.json"
24
+ ],
25
+ "directories": {
26
+ "output": "release"
27
+ },
28
+ "extraResources": [
29
+ {
30
+ "from": "dist/resources/bin",
31
+ "to": "bin",
32
+ "filter": ["*.exe"]
33
+ }
34
+ ]
35
+ },
36
+ "author": "Marcin Karpiński <mkarpins@gmail.com>",
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "@emotion/react": "^11.14.0",
40
+ "@emotion/styled": "^11.14.0",
41
+ "@mui/icons-material": "^6.4.2",
42
+ "@mui/material": "^6.4.2",
43
+ "classnames": "^2.5.1",
44
+ "electron-devtools-installer": "^4.0.0",
45
+ "electron-store": "^8.2.0",
46
+ "fs-extra": "^11.3.0",
47
+ "i18next": "^24.2.2",
48
+ "i18next-node-fs-backend": "^2.1.3",
49
+ "jsonschema": "^1.5.0",
50
+ "lodash": "^4.17.21",
51
+ "mkdirp": "^3.0.1",
52
+ "moment": "^2.30.1",
53
+ "moment-duration-format": "^2.3.2",
54
+ "puppeteer": "^24.1.1",
55
+ "puppeteer-core": "^24.1.1",
56
+ "puppeteer-extra": "^3.3.6",
57
+ "puppeteer-extra-plugin-devtools": "^2.4.6",
58
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
59
+ "react": "^19.0.0",
60
+ "react-dom": "^19.0.0",
61
+ "react-i18next": "^15.4.0",
62
+ "react-number-format": "^5.4.3",
63
+ "react-router-dom": "^7.1.5",
64
+ "usehooks-ts": "^3.1.0",
65
+ "yt-dlp-wrap": "^2.3.12"
66
+ },
67
+ "devDependencies": {
68
+ "@types/eslint": "^9.6.1",
69
+ "@types/fs-extra": "^11.0.4",
70
+ "@types/i18next": "^13.0.0",
71
+ "@types/i18next-node-fs-backend": "^2.1.5",
72
+ "@types/jsonschema": "^1.1.1",
73
+ "@types/lodash": "^4.17.15",
74
+ "@types/moment-duration-format": "^2.2.6",
75
+ "@types/node": "^22.13.1",
76
+ "@types/prettier": "^3.0.0",
77
+ "@types/puppeteer-core": "^7.0.4",
78
+ "@types/react": "^19.0.8",
79
+ "@types/react-dom": "^19.0.3",
80
+ "@types/webpack-dev-server": "^4.7.2",
81
+ "@types/webpack-env": "^1.18.8",
82
+ "@typescript-eslint/eslint-plugin": "^8.23.0",
83
+ "@typescript-eslint/parser": "^8.23.0",
84
+ "concurrently": "^9.1.2",
85
+ "copy-webpack-plugin": "^12.0.2",
86
+ "cross-env": "^7.0.3",
87
+ "css-loader": "^7.1.2",
88
+ "electron": "^34.0.2",
89
+ "electron-builder": "^25.1.8",
90
+ "eslint": "^9.19.0",
91
+ "eslint-config-prettier": "^10.0.1",
92
+ "eslint-plugin-css": "^0.11.0",
93
+ "eslint-plugin-react": "^7.37.4",
94
+ "eslint-webpack-plugin": "^4.2.0",
95
+ "html-webpack-plugin": "^5.6.3",
96
+ "i18next-scanner-webpack": "^0.9.1",
97
+ "mini-css-extract-plugin": "^2.9.2",
98
+ "prettier": "^3.4.2",
99
+ "raw-loader": "^4.0.2",
100
+ "rimraf": "^6.0.1",
101
+ "source-map-loader": "^5.0.0",
102
+ "stylus": "^0.64.0",
103
+ "stylus-loader": "^8.1.1",
104
+ "ts-loader": "^9.5.2",
105
+ "ts-node": "^10.9.2",
106
+ "ts-node-dev": "^2.0.0",
107
+ "typescript": "^5.7.3",
108
+ "wait-on": "^8.0.2",
109
+ "webpack": "^5.97.1",
110
+ "webpack-cli": "^6.0.1",
111
+ "webpack-dev-server": "^5.2.0",
112
+ "webpack-node-externals": "^3.0.0"
113
+ },
114
+ "packageManager": "yarn@4.4.1"
115
+ }
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta
7
+ http-equiv="Content-Security-Policy"
8
+ content="default-src 'self' 'unsafe-inline'; img-src * 'self' ; script-src 'self' 'unsafe-eval'; style-src-elem 'self' 'unsafe-inline';"
9
+ />
10
+ <title>Youtube Grabber</title>
11
+ <link rel="icon" type="image/png" href="/resources/icons/favicon-32x32.png" />
12
+
13
+ <!--[if IE]>
14
+ <link rel="SHORTCUT ICON" href="/resources/icons/favicon.ico" />
15
+ <![endif]-->
16
+ </head>
17
+ <body>
18
+ <div id="root"></div>
19
+ </body>
20
+ </html>
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,7 @@
1
+ import ElectronStore from "electron-store";
2
+
3
+ import {IStore} from "../common/Store";
4
+
5
+ declare global {
6
+ var store: ElectronStore<IStore>;
7
+ }
@@ -0,0 +1 @@
1
+ declare module "i18next-scanner-webpack";
@@ -0,0 +1,4 @@
1
+ declare module "*.styl" {
2
+ const styles: { [key: string]: string };
3
+ export default styles;
4
+ }
@@ -0,0 +1 @@
1
+ declare module "*.svg";
package/src/App.styl ADDED
@@ -0,0 +1,24 @@
1
+ @import "./styles/mixins.styl"
2
+ @import "./styles/fonts.styl"
3
+
4
+ html
5
+ font-size: 1rem
6
+ font-family: "Lato"
7
+
8
+ body
9
+ margin: 0
10
+ padding: 0
11
+ no-select()
12
+
13
+ :global(#root)
14
+ position: absolute
15
+ width: 100%
16
+ height: 100%
17
+ overflow: hidden
18
+
19
+ .app
20
+ background-color: var(--theme-palette-background-paper);
21
+ display: flex
22
+ flex-direction: column
23
+ height: 100vh
24
+ width: 100vw
package/src/App.tsx ADDED
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import {HashRouter, Route, Routes} from "react-router-dom";
3
+
4
+ import {Box, CssBaseline} from "@mui/material";
5
+
6
+ import Styles from "./App.styl";
7
+ import AppBar from "./components/appBar/AppBar";
8
+ import {useAppContext} from "./react/contexts/AppContext";
9
+ import DevelopmentView from "./views/development/DevelopmentView";
10
+ import {HomeView} from "./views/home/HomeView";
11
+ import SettingsView from "./views/settings/SettingsView";
12
+
13
+ export const App: React.FC = () => {
14
+ const { state } = useAppContext();
15
+
16
+ return (
17
+ <HashRouter>
18
+ <Box className={Styles.app}>
19
+ <CssBaseline enableColorScheme />
20
+ <AppBar />
21
+ <Routes location={state.location}>
22
+ <Route path="/" element={<HomeView />} />
23
+ <Route path="/settings" element={<SettingsView />} />
24
+ <Route path="/development" element={<DevelopmentView />} />
25
+ </Routes>
26
+ </Box>
27
+ </HashRouter>
28
+ );
29
+ };
30
+
31
+ export default App;
@@ -0,0 +1,30 @@
1
+ import moment from "moment";
2
+ import momentDurationFormat from "moment-duration-format";
3
+ import React from "react";
4
+ import {I18nextProvider} from "react-i18next";
5
+
6
+ import App from "./App";
7
+ import i18n from "./i18next";
8
+ import {AppContextProvider} from "./react/contexts/AppContext";
9
+ import {AppThemeProvider} from "./react/contexts/AppThemeContext";
10
+ import {DataProvider} from "./react/contexts/DataContext";
11
+
12
+ export const Bootstrap: React.FC = () => {
13
+ momentDurationFormat(moment as any);
14
+ moment.updateLocale("en", {week: {dow: 1}});
15
+ i18n.changeLanguage(global.store.get("application").language);
16
+
17
+ return (
18
+ <AppContextProvider>
19
+ <DataProvider>
20
+ <I18nextProvider i18n={i18n}>
21
+ <AppThemeProvider>
22
+ <App />
23
+ </AppThemeProvider>
24
+ </I18nextProvider>
25
+ </DataProvider>
26
+ </AppContextProvider>
27
+ );
28
+ };
29
+
30
+ export default App;
@@ -0,0 +1,22 @@
1
+ export interface ICancelablePromise {
2
+ promise: Promise<any>;
3
+ cancel: () => void;
4
+ }
5
+
6
+ export const cancellablePromise = (promise: Promise<any>): ICancelablePromise => {
7
+ let isCanceled = false;
8
+
9
+ const wrappedPromise = new Promise((resolve, reject) => {
10
+ promise.then(
11
+ (value) => (isCanceled ? reject({ isCanceled, value }) : resolve(value)),
12
+ (error) => reject({ isCanceled, error }),
13
+ );
14
+ });
15
+
16
+ return {
17
+ promise: wrappedPromise,
18
+ cancel: () => (isCanceled = true),
19
+ };
20
+ };
21
+
22
+ export default cancellablePromise;
@@ -0,0 +1,8 @@
1
+ export enum ComponentDisplayMode {
2
+ Minimal = 0,
3
+ Compact = 1,
4
+ Extended = 2,
5
+ Full = 3,
6
+ }
7
+
8
+ export default ComponentDisplayMode;
@@ -0,0 +1,3 @@
1
+ export const delay = (miliseconds: number) => new Promise((resolve) => setTimeout(resolve, miliseconds));
2
+
3
+ export default delay;
@@ -0,0 +1,171 @@
1
+ import fs from "fs-extra";
2
+ import http from "http";
3
+ import https from "https";
4
+ import $_ from "lodash";
5
+ import {mkdirp} from "mkdirp";
6
+ import path from "path";
7
+
8
+ const getDirName = path.dirname;
9
+
10
+ export const readFileAsync = (filename: string, encoding: BufferEncoding = "utf-8") => {
11
+ return new Promise<string>((resolve, reject) => {
12
+ try {
13
+ fs.readFile(filename, { encoding }, (error: any, buffer: any) => {
14
+ if (error) resolve("");
15
+ else resolve(buffer);
16
+ });
17
+ } catch (error) {
18
+ reject(error);
19
+ }
20
+ });
21
+ };
22
+
23
+ export const readFileSync = (filename: string, encoding: BufferEncoding = "utf-8") => {
24
+ return fs.readFileSync(filename, { encoding });
25
+ };
26
+
27
+ export const writeFileAsync = (data: any, filename: string, encoding?: BufferEncoding, mode: any = 0o775) => {
28
+ return new Promise(async (resolve, reject) => {
29
+ try {
30
+ const mkdirResult = await mkdirp(getDirName(filename), { mode: 0o775 });
31
+
32
+ fs.writeFile(filename, data, { encoding, mode }, (error: any, result?: any) => {
33
+ if (error) reject(error.message);
34
+ else resolve(result);
35
+ });
36
+ } catch (error) {
37
+ reject(error);
38
+ }
39
+ });
40
+ };
41
+
42
+ export const writeStreamAsync = (url: string, filename: string, encoding?: BufferEncoding) => {
43
+ return new Promise(async (resolve, reject) => {
44
+ try {
45
+ const mkdirResult = await mkdirp(getDirName(filename), { mode: 0o775 });
46
+
47
+ const file = fs.createWriteStream(filename as any, { encoding });
48
+ file.on("error", (error: any) => {
49
+ reject(error.message);
50
+ });
51
+ const action = $_.startsWith(url, "https") ? https : http;
52
+
53
+ return action.get(url, (response) => {
54
+ response.on("error", (error) => {
55
+ fs.unlinkSync(filename);
56
+ reject(error);
57
+ });
58
+
59
+ response.pipe(file);
60
+ file.on("finish", () => {
61
+ resolve(file);
62
+ });
63
+ });
64
+ } catch (error) {
65
+ reject(error);
66
+ }
67
+ });
68
+ };
69
+
70
+ export const copy = async (src: string, dest: string) => {
71
+ const ensurePath = await fs.mkdirp(path.dirname(dest));
72
+
73
+ return fs.copyFile(src, dest);
74
+ };
75
+
76
+ export const findFiles = async (
77
+ pathname: string,
78
+ options?: { filter: string[] },
79
+ ): Promise<Array<{ id: string; name: string; path: string }>> => {
80
+ const fileList: Array<{ id: string; name: string; path: string }> = [];
81
+
82
+ if (!fs.existsSync(pathname)) {
83
+ throw new Error("Directory does not exists!");
84
+ }
85
+ const filter = $_.get(options, "filter");
86
+ const files = await fs.readdir(pathname);
87
+
88
+ for (const file of files) {
89
+ const filename = path.join(pathname, file);
90
+ const stat = fs.lstatSync(filename);
91
+
92
+ if (stat.isDirectory()) {
93
+ fileList.push(...(await findFiles(filename, options)));
94
+ } else if ($_.includes(filter, path.extname(filename))) {
95
+ fileList.push({
96
+ id: path.relative("public", filename),
97
+ name: path.parse(filename).name,
98
+ path: path.relative("public", filename),
99
+ });
100
+ }
101
+ }
102
+
103
+ return fileList;
104
+ };
105
+
106
+ export const createDirectory = (pathname: string) => {
107
+ return mkdirp(getDirName(pathname), { mode: 0o775 });
108
+ };
109
+
110
+ export const removeDirectory = (pathname: string) => {
111
+ return new Promise(async (resolve, reject) => {
112
+ if (!fs.existsSync(pathname)) {
113
+ resolve("Directory does not exists!");
114
+ }
115
+ const err = (await fs.remove(getDirName(pathname))) as any;
116
+
117
+ if (err) return reject(err);
118
+
119
+ resolve(pathname);
120
+ });
121
+ };
122
+
123
+ export const remove = (filename: string) => {
124
+ return new Promise((resolve, reject) => {
125
+ try {
126
+ if (!fs.existsSync(filename)) {
127
+ return resolve(filename);
128
+ }
129
+ fs.unlink(filename, (error) => {
130
+ if (error) reject(error);
131
+ else resolve(filename);
132
+ });
133
+ } catch (error) {
134
+ reject(error);
135
+ }
136
+ });
137
+ };
138
+
139
+ // export const getDirectorySize = (pathname: string) => {
140
+ // return new Promise((resolve, reject) => {
141
+ // try {
142
+ // getSize(pathname, (error: any, size: number) => {
143
+ // if (error) return reject(error);
144
+
145
+ // return resolve(size);
146
+ // });
147
+ // } catch (error) {
148
+ // reject(error);
149
+ // }
150
+ // });
151
+ // };
152
+
153
+ // export const getUserDirectorySize = (userId: string, host: string) => {
154
+ // return getDirectorySize(`${host}/data/${userId}`);
155
+ // };
156
+
157
+ // export const downloadAndSaveImage = (sourceUrl: string, targetPath: string, encoding: string = "base64") => {
158
+ // return writeStreamAsync(sourceUrl, targetPath, encoding);
159
+ // };
160
+
161
+ // export const downloadAndSaveFile = (sourceUrl: string, targetPath: string, encoding?: string) => {
162
+ // return writeStreamAsync(sourceUrl, targetPath, encoding);
163
+ // };
164
+
165
+ // export const saveImage = (imageData: string, targetPath: string, encoding: string = "base64") => {
166
+ // if ($_.startsWith(imageData, "http")) {
167
+ // return downloadAndSaveImage(imageData, targetPath, encoding);
168
+ // }
169
+
170
+ // return writeFileAsync(imageData, targetPath, encoding);
171
+ // };