├── .eslintignore
├── .github
├── FUNDING.yml
├── images
│ ├── logo
│ │ ├── 128x128.png
│ │ └── 256x256.png
│ ├── sentry
│ │ ├── sentry_01.png
│ │ └── sentry_02.png
│ ├── platform
│ │ ├── apple_16x16.png
│ │ ├── apple_32x32.png
│ │ ├── linux_16x16.png
│ │ ├── linux_32x32.png
│ │ ├── windows_16x16.png
│ │ └── windows_32x32.png
│ ├── screenshots
│ │ ├── ui_0.1.0.png
│ │ ├── ui_0.10.0.png
│ │ ├── ui_0.2.0.png
│ │ ├── ui_0.3.0.png
│ │ ├── ui_0.4.0.png
│ │ ├── ui_0.5.0.png
│ │ ├── ui_0.6.0.png
│ │ ├── ui_0.7.0.png
│ │ ├── ui_0.8.0.png
│ │ ├── ui_0.9.0.png
│ │ └── ui_latest.png
│ └── social_preview_github
│ │ ├── social_preview.png
│ │ └── social_preview.xcf
├── ISSUE_TEMPLATE
│ ├── other.md
│ ├── question.md
│ ├── feature_request.md
│ └── bug_report.md
├── workflows
│ ├── av_scan.yml
│ ├── greetings.yml
│ ├── npm_audit.yml
│ ├── mark_stale.yml
│ ├── jsdoc.yml
│ └── electron_builder.yml
├── .pre-commit-config.yaml
├── PULL_REQUEST_TEMPLATE
│ └── pull_request_template.md
└── CODE_OF_CONDUCT.md
├── app
├── img
│ ├── dummy
│ │ └── dummy.png
│ ├── icon
│ │ └── icon.png
│ ├── about
│ │ ├── icon_about.png
│ │ └── old
│ │ │ └── icon_about_.png
│ ├── menu
│ │ ├── edit
│ │ │ ├── copy_red.png
│ │ │ ├── cut_red.png
│ │ │ ├── redo_red.png
│ │ │ ├── undo_red.png
│ │ │ ├── paste_red.png
│ │ │ ├── redo.svg
│ │ │ ├── copy.svg
│ │ │ ├── paste.svg
│ │ │ ├── undo.svg
│ │ │ └── cut.svg
│ │ ├── file
│ │ │ ├── cog_red.png
│ │ │ ├── power-off_red.png
│ │ │ ├── power-off.svg
│ │ │ └── cog.svg
│ │ ├── help
│ │ │ ├── github_red.png
│ │ │ ├── youtube_red.png
│ │ │ ├── terminal_red.png
│ │ │ ├── info-circle_red.png
│ │ │ ├── cloud-download-alt_red.png
│ │ │ ├── info-circle.svg
│ │ │ ├── terminal.svg
│ │ │ ├── cloud-download-alt.svg
│ │ │ ├── youtube.svg
│ │ │ └── github.svg
│ │ └── window
│ │ │ ├── window-maximize_red.png
│ │ │ ├── window-minimize_red.png
│ │ │ ├── window-minimize.svg
│ │ │ └── window-maximize.svg
│ ├── notification
│ │ ├── icon.png
│ │ └── old
│ │ │ └── icon_.png
│ └── background
│ │ └── icon_textarea_log_64x64.png
├── fonts
│ ├── Arial
│ │ └── Arial.ttf
│ └── ArialMono
│ │ └── Arial_Monospaced_MT.ttf
├── css
│ ├── distraction.css
│ └── core.css
├── js
│ └── modules
│ │ ├── ffmpeg.js
│ │ ├── crashReporter.js
│ │ ├── unhandled.js
│ │ ├── sentry.js
│ │ ├── urls.js
│ │ ├── settings.js
│ │ ├── youtubeDl.js
│ │ └── utils.js
├── distraction.html
├── settings.html
└── index.html
├── resources
└── installer
│ ├── icon.ico
│ ├── icon.icns
│ ├── icons
│ ├── 16x16.png
│ ├── 24x24.png
│ ├── 32x32.png
│ ├── 48x48.png
│ ├── 64x64.png
│ ├── 128x128.png
│ ├── 256x256.png
│ ├── 512x512.png
│ └── 1024x1024.png
│ ├── dmg_background.png
│ ├── nsis
│ └── installer.nsh
│ └── base_material
│ └── app_icon
│ ├── File_Circle-icons-download_svg_Wikipedia.desktop
│ └── Circle-icons-download.svg
├── .gitignore
├── test
├── README.md
└── spec.js
├── .codacy.yml
├── docs
├── CONTRIBUTORS.md
├── SECURITY.md
├── BUILD.md
├── VERBOSE.md
├── NOTES.md
├── UI-HISTORY.md
├── INSTALL.md
├── SENTRY.md
├── CONTRIBUTING.md
└── CHANGELOG.md
├── .editorconfig
├── .eslintrc.json
├── jsdoc.json
├── README.md
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [yafp]
2 | patreon: yafp
3 |
--------------------------------------------------------------------------------
/app/img/dummy/dummy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/dummy/dummy.png
--------------------------------------------------------------------------------
/app/img/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/icon/icon.png
--------------------------------------------------------------------------------
/app/fonts/Arial/Arial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/fonts/Arial/Arial.ttf
--------------------------------------------------------------------------------
/app/img/about/icon_about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/about/icon_about.png
--------------------------------------------------------------------------------
/resources/installer/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #directories
2 | #
3 | node_modules
4 | dist
5 |
6 | #files
7 | #
8 | #*.tgz
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/img/menu/edit/copy_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/edit/copy_red.png
--------------------------------------------------------------------------------
/app/img/menu/edit/cut_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/edit/cut_red.png
--------------------------------------------------------------------------------
/app/img/menu/edit/redo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/edit/redo_red.png
--------------------------------------------------------------------------------
/app/img/menu/edit/undo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/edit/undo_red.png
--------------------------------------------------------------------------------
/app/img/menu/file/cog_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/file/cog_red.png
--------------------------------------------------------------------------------
/app/img/notification/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/notification/icon.png
--------------------------------------------------------------------------------
/resources/installer/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icon.icns
--------------------------------------------------------------------------------
/.github/images/logo/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/logo/128x128.png
--------------------------------------------------------------------------------
/.github/images/logo/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/logo/256x256.png
--------------------------------------------------------------------------------
/app/img/about/old/icon_about_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/about/old/icon_about_.png
--------------------------------------------------------------------------------
/app/img/menu/edit/paste_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/edit/paste_red.png
--------------------------------------------------------------------------------
/app/img/menu/help/github_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/help/github_red.png
--------------------------------------------------------------------------------
/app/img/menu/help/youtube_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/help/youtube_red.png
--------------------------------------------------------------------------------
/.github/images/sentry/sentry_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/sentry/sentry_01.png
--------------------------------------------------------------------------------
/.github/images/sentry/sentry_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/sentry/sentry_02.png
--------------------------------------------------------------------------------
/app/img/menu/file/power-off_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/file/power-off_red.png
--------------------------------------------------------------------------------
/app/img/menu/help/terminal_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/help/terminal_red.png
--------------------------------------------------------------------------------
/app/img/notification/old/icon_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/notification/old/icon_.png
--------------------------------------------------------------------------------
/resources/installer/icons/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/16x16.png
--------------------------------------------------------------------------------
/resources/installer/icons/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/24x24.png
--------------------------------------------------------------------------------
/resources/installer/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/32x32.png
--------------------------------------------------------------------------------
/resources/installer/icons/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/48x48.png
--------------------------------------------------------------------------------
/resources/installer/icons/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/64x64.png
--------------------------------------------------------------------------------
/app/img/menu/help/info-circle_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/help/info-circle_red.png
--------------------------------------------------------------------------------
/resources/installer/dmg_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/dmg_background.png
--------------------------------------------------------------------------------
/resources/installer/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/128x128.png
--------------------------------------------------------------------------------
/resources/installer/icons/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/256x256.png
--------------------------------------------------------------------------------
/resources/installer/icons/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/512x512.png
--------------------------------------------------------------------------------
/.github/images/platform/apple_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/platform/apple_16x16.png
--------------------------------------------------------------------------------
/.github/images/platform/apple_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/platform/apple_32x32.png
--------------------------------------------------------------------------------
/.github/images/platform/linux_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/platform/linux_16x16.png
--------------------------------------------------------------------------------
/.github/images/platform/linux_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/platform/linux_32x32.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.1.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.1.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.10.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.10.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.2.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.2.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.3.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.3.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.4.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.4.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.5.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.5.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.6.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.6.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.7.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.7.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.8.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.8.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_0.9.0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_0.9.0.png
--------------------------------------------------------------------------------
/.github/images/screenshots/ui_latest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/screenshots/ui_latest.png
--------------------------------------------------------------------------------
/resources/installer/icons/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/resources/installer/icons/1024x1024.png
--------------------------------------------------------------------------------
/resources/installer/nsis/installer.nsh:
--------------------------------------------------------------------------------
1 | !macro customHeader
2 | ShowInstDetails show
3 | ShowUninstDetails show
4 | !macroend
5 |
--------------------------------------------------------------------------------
/.github/images/platform/windows_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/platform/windows_16x16.png
--------------------------------------------------------------------------------
/.github/images/platform/windows_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/platform/windows_32x32.png
--------------------------------------------------------------------------------
/app/fonts/ArialMono/Arial_Monospaced_MT.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/fonts/ArialMono/Arial_Monospaced_MT.ttf
--------------------------------------------------------------------------------
/app/img/menu/window/window-maximize_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/window/window-maximize_red.png
--------------------------------------------------------------------------------
/app/img/menu/window/window-minimize_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/window/window-minimize_red.png
--------------------------------------------------------------------------------
/app/img/menu/help/cloud-download-alt_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/menu/help/cloud-download-alt_red.png
--------------------------------------------------------------------------------
/app/img/background/icon_textarea_log_64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/app/img/background/icon_textarea_log_64x64.png
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Other issue template
3 | about: Anything else
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
--------------------------------------------------------------------------------
/.github/images/social_preview_github/social_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/social_preview_github/social_preview.png
--------------------------------------------------------------------------------
/.github/images/social_preview_github/social_preview.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yafp/media-dupes/HEAD/.github/images/social_preview_github/social_preview.xcf
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 |
5 | ## /test/
6 |
7 | This folder contains test specifications.
8 |
--------------------------------------------------------------------------------
/app/img/menu/window/window-minimize.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: You have a question - ask it right away
4 | title: '[Question]'
5 | labels: 'question'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Question**
11 | ...
12 |
--------------------------------------------------------------------------------
/app/css/distraction.css:
--------------------------------------------------------------------------------
1 | /* ########################################################################## */
2 | /* BODY */
3 | /* ########################################################################## */
4 | body{
5 | background-color: black
6 | }
7 |
--------------------------------------------------------------------------------
/.codacy.yml:
--------------------------------------------------------------------------------
1 | ---
2 | exclude_paths:
3 | - 'app/lib/**'
4 | - 'docs/**'
5 | - 'docs/**/*'
6 | - 'docs/jsdocs/**/*'
7 | - '.github/**'
8 | - '.github/**/*'
9 | - 'test/*'
10 | - 'LICENSE'
11 | - 'CODE_OF_CONDUCT.md'
12 | - 'README.md'
13 | - 'jsdoc.json'
14 |
--------------------------------------------------------------------------------
/app/img/menu/window/window-maximize.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 |
5 | ## contributors
6 |
7 | ### code
8 | #### core
9 | * [yafp](https://github.com/yafp/)
10 |
11 |
12 | #### testing
13 | * G
14 |
--------------------------------------------------------------------------------
/docs/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Only the latest version of media-dupes is supported at all.
6 |
7 | ## Reporting a Vulnerability
8 | Feel free to open an issue and report the vulnerability you have found. In best case featuring a small demo.
9 |
--------------------------------------------------------------------------------
/resources/installer/base_material/app_icon/File_Circle-icons-download_svg_Wikipedia.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Version=1.0
3 | Type=Link
4 | Name=File:Circle-icons-download.svg - Wikipedia
5 | Comment=
6 | Icon=user-bookmarks
7 | URL=https://en.m.wikipedia.org/wiki/File:Circle-icons-download.svg
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | # top-most EditorConfig file
3 | root = true
4 |
5 | # valid for all files
6 | [*]
7 | indent_style = space
8 | indent_size = 4
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 |
14 | # valid only for markdown files
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/app/img/menu/edit/redo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/menu/edit/copy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/menu/help/info-circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/menu/help/terminal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/menu/help/cloud-download-alt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "commonjs": true,
6 | "jquery": true
7 | },
8 | "extends": "eslint:recommended",
9 | "globals": {
10 | "Atomics": "readonly",
11 | "SharedArrayBuffer": "readonly"
12 | },
13 | "parserOptions": {
14 | "ecmaVersion": 2018,
15 | "sourceType": "module"
16 | },
17 | "rules": {
18 | "no-console": 0,
19 | "no-unused-vars": 0,
20 | "indent": ["error", 4]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/img/menu/file/power-off.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/menu/help/youtube.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/menu/edit/paste.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/menu/edit/undo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/av_scan.yml:
--------------------------------------------------------------------------------
1 | # ----------------------------------------------------------------------------
2 | # This action / workflow:
3 | # - does a simple av scan
4 | # - on each push to the master branch
5 | # - using clamAV
6 | #
7 | # Using: https://github.com/marketplace/actions/git-anti-virus-scan
8 | # ----------------------------------------------------------------------------
9 |
10 | name: av_scan
11 |
12 | on: [push]
13 |
14 | jobs:
15 | gitavscan:
16 | runs-on: ubuntu-latest
17 | name: AV scan
18 | steps:
19 | - uses: actions/checkout@v2
20 | - name: Git AV Scan
21 | uses: djdefi/gitavscan@v1
22 |
--------------------------------------------------------------------------------
/app/img/menu/edit/cut.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: '[Feature request]'
5 | labels: 'enhancement'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is.
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # PRE-COMMIT-HOOK
2 | #
3 | # See: https://github.com/pre-commit/pre-commit-hooks
4 | #
5 | - repo: https://github.com/pre-commit/pre-commit-hooks
6 | rev: v2.5.0 # Use the ref you want to point at
7 | hooks:
8 | - id: check-case-conflict # Check for files with names that would conflict on a case-insensitive filesystem like MacOS HFS+ or Windows FAT.
9 | - id: check-docstring-first # Checks for a common error of placing code before the docstring.
10 | - id: check-json # Attempts to load all json files to verify syntax.
11 | - id: check-symlinks # Checks for symlinks which do not point to anything.
12 | - id: check-yaml # Attempts to load all yaml files to verify syntax.
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: '[Bug]'
5 | labels: 'bug'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. Windows 10]
28 | - media-dupes version [e.g. 0.1.0]
29 |
30 | **Additional context**
31 | Add any other context about the problem here.
32 |
--------------------------------------------------------------------------------
/.github/workflows/greetings.yml:
--------------------------------------------------------------------------------
1 | # ----------------------------------------------------------------------------
2 | # This action / workflow:
3 | # - greets first time issuer
4 | # - greets first time PR
5 | #
6 | # Using: https://github.com/marketplace/actions/first-interaction
7 | # ----------------------------------------------------------------------------
8 |
9 | name: greetings
10 |
11 | on: [pull_request, issues]
12 |
13 | jobs:
14 | greeting:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/first-interaction@v1
18 | with:
19 | repo-token: ${{ secrets.GITHUB_TOKEN }}
20 | issue-message: 'Thank you very much for your first issue to this project. It is much appreciated.'
21 | pr-message: 'Thank you very much for your first pull request to this project. It is much appreciated.'
22 |
--------------------------------------------------------------------------------
/.github/workflows/npm_audit.yml:
--------------------------------------------------------------------------------
1 | # ----------------------------------------------------------------------------
2 | # This action / workflow:
3 | # - executes npm audit
4 | # - on each push to the master branch
5 | #
6 | # Using: https://github.com/marketplace/actions/npm-audit-action
7 | # ----------------------------------------------------------------------------
8 |
9 | name: npm_audit
10 |
11 | on:
12 | pull_request:
13 | push:
14 | branches:
15 | - master
16 | jobs:
17 | scan:
18 | name: npm audit
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v1
22 | - name: install dependencies
23 | run: npm ci
24 | - uses: oke-py/npm-audit-action@v1.1.0
25 | with:
26 | github_token: ${{ secrets.GITHUB_TOKEN }}
27 | issue_assignees: oke-py
28 | issue_labels: vulnerability,test
29 |
--------------------------------------------------------------------------------
/app/img/menu/file/cog.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/BUILD.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 | ## Build
5 |
6 | ### Warning
7 | Be aware that you have to **package** and **build** on the platform you want to build for.
8 | Bulding windows on linux works in theory, but this is causing trouble with the ```youtube-dl``` binary.
9 | See [#47](https://github.com/yafp/media-dupes/issues/47) for more details.
10 |
11 | ### Pre
12 | ```
13 | # Cleaning up node_modules
14 | npm run reinstall
15 |
16 | # Check synthax of .js files
17 | npm run standardx
18 | ```
19 |
20 | ### Packaging
21 | ```
22 | # linux
23 | npm run pack-linux
24 |
25 | # mac
26 | npm run pack-mac
27 |
28 | # windows
29 | npm run pack-win
30 | ```
31 |
32 |
33 |
34 | ### Building
35 |
36 | ```
37 | # linux
38 | npm run build-linux
39 |
40 | # mac
41 | npm run build-mac
42 |
43 | # windows
44 | npm run build-win
45 | ```
46 |
--------------------------------------------------------------------------------
/app/js/modules/ffmpeg.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Contains all ffmpeg functions
3 | * @author yafp
4 | * @module ffmpeg
5 | */
6 | 'use strict'
7 |
8 | // ----------------------------------------------------------------------------
9 | // REQUIRE MODULES
10 | // ----------------------------------------------------------------------------
11 | //
12 |
13 | /**
14 | * @function ffmpegGetBinaryPath
15 | * @summary Get the binary path of the ffmpeg binary
16 | * @description Get the binary path of the ffmpeg binary
17 | * @return {string} ffmpeg.path - The path to the ffmpeg binary
18 | */
19 | function ffmpegGetBinaryPath () {
20 | var ffmpeg = require('ffmpeg-static-electron')
21 | return (ffmpeg.path)
22 | }
23 |
24 | // ----------------------------------------------------------------------------
25 | // EXPORT THE MODULE FUNCTIONS
26 | // ----------------------------------------------------------------------------
27 | //
28 | module.exports.ffmpegGetBinaryPath = ffmpegGetBinaryPath
29 |
--------------------------------------------------------------------------------
/.github/workflows/mark_stale.yml:
--------------------------------------------------------------------------------
1 | # ----------------------------------------------------------------------------
2 | # This action / workflow:
3 | # - checks for stale issues
4 | # - and pull requests
5 | # - labels them stale after x days
6 | # - closed them after Y days
7 | #
8 | # Using: https://github.com/marketplace/actions/close-stale-issues
9 | # ----------------------------------------------------------------------------
10 |
11 | name: mark_stale
12 |
13 | on:
14 | schedule:
15 | - cron: "0 0 * * *"
16 |
17 | jobs:
18 | stale:
19 |
20 | runs-on: ubuntu-latest
21 |
22 | steps:
23 | - uses: actions/stale@v1
24 | with:
25 | repo-token: ${{ secrets.GITHUB_TOKEN }}
26 | stale-issue-message: 'This issue seems to be stale'
27 | stale-pr-message: 'This pull request seems to be stale'
28 | stale-issue-label: 'no-issue-activity'
29 | stale-pr-label: 'no-pr-activity'
30 | days-before-stale: 90
31 | days-before-close: 365
32 |
--------------------------------------------------------------------------------
/docs/VERBOSE.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 | ## Verbose
5 |
6 | There are several ways to get verbose informations about what **media-dupes** is doing.
7 |
8 | * Log files
9 | * Developer console
10 | * Commandline output
11 |
12 | ### Log files
13 | **media-dupes** is writing log files. You will find them here:
14 |
15 | * Linux:
16 | * `~/.config/media-dupes/logs/main.log`
17 | * `~/.config/media-dupes/logs/renderer.log`
18 | * macOS:
19 | * `~/Library/Logs/media-dupes/main.log`
20 | * `~/Library/Logs/media-dupes/renderer.log`
21 | * Windows:
22 | * `%USERPROFILE%\AppData\Roaming\media-dupes\logs\main.log`
23 | * `%USERPROFILE%\AppData\Roaming\media-dupes\logs\renderer.log`
24 |
25 | ### Developer console
26 | Press F12 within the app or navigate via the `Help` menu and select `Console (F12)` to access the Developer Console.
27 |
28 | ### Commandline output
29 | * Start **media-dupes** with `--verbose`
30 |
--------------------------------------------------------------------------------
/docs/NOTES.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 | ## developer notes / reminder
5 |
6 | ### todo
7 | * re-enable ASAR
8 | * Is currently disabled cause otherwise in packaged mode the code does not find the youtube-dl executable
9 | * Current output: asar using is disabled — it is strongly not recommended solution=enable asar and use asarUnpack to unpack files that must be externally available
10 |
11 | ### ideas / brainstorming
12 | * Dependencies:
13 | * youtube-dl- node: exec has a callback. https://github.com/przemyslawpluta/node-youtube-dl/issues/216
14 | * ffmpeg: make pull request to https://github.com/pietrop/ffmpeg-static-electron to update from ffmpeg 3.x to 4.x
15 | * 20191210 - Created pull request for ffmpeg 4.2.1
16 | * Progress for stream (non exec use): https://www.npmjs.com/package/progress-stream
17 | * seperate electron-builder.conf from package-json
18 |
19 | ### urls for screenshots:
20 | * https://www.youtube.com/watch?v=WY0Sap4Q0sg
21 | * https://soundcloud.com/jdilla/thank-you-jay-dee-act-3
22 |
--------------------------------------------------------------------------------
/.github/workflows/jsdoc.yml:
--------------------------------------------------------------------------------
1 | # ----------------------------------------------------------------------------
2 | # This action / workflow:
3 | # - generates a jsdoc based documentation
4 | # - on each commit to the master branch
5 | # - and uploads it to the project github-page
6 | #
7 | # Using: https://github.com/marketplace/actions/jsdoc-action
8 | #
9 | # See: https://yafp.github.io/media-dupes/
10 | # ----------------------------------------------------------------------------
11 |
12 | name: jsdoc
13 |
14 | on:
15 | push:
16 | branches:
17 | - master
18 |
19 | jobs:
20 | deploy:
21 | runs-on: ubuntu-latest
22 | steps:
23 | - name: Checkout code
24 | uses: actions/checkout@v2
25 |
26 | - name: Build
27 | uses: andstor/jsdoc-action@v1
28 | with:
29 | output_dir: ./out
30 | config_file: jsdoc.json
31 | template_name: docdash
32 | front_page: README.md
33 |
34 | - name: Deploy
35 | uses: peaceiris/actions-gh-pages@v3
36 | with:
37 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
38 | publish_dir: ./out
39 |
--------------------------------------------------------------------------------
/app/js/modules/crashReporter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file crashReporter.js
3 | * @fileOverview This module contains all crashReporter functions, See: https://electronjs.org/docs/api/crash-reporter
4 | * @author yafp
5 | * @module crashReporter
6 | */
7 |
8 | 'use strict'
9 |
10 | /**
11 | * @function initCrashReporter
12 | * @summary Initialized the crashReporter
13 | * @description Initialized the electron crashReporter. See: https://electronjs.org/docs/api/crash-reporter
14 | */
15 | function initCrashReporter () {
16 | const { crashReporter } = require('electron')
17 | crashReporter.start({
18 | productName: 'media-dupes',
19 | companyName: 'yafp',
20 | submitURL: 'https://sentry.io/api/1757940/minidump/?sentry_key=bbaa8fa09ca84a8da6a545c04d086859',
21 | uploadToServer: false
22 | })
23 |
24 | // To simulate a crash - execute: process.crash();
25 | }
26 |
27 | // ----------------------------------------------------------------------------
28 | // EXPORT THE MODULE FUNCTIONS
29 | // ----------------------------------------------------------------------------
30 | //
31 | module.exports.initCrashReporter = initCrashReporter
32 |
--------------------------------------------------------------------------------
/app/img/menu/help/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
4 |
5 | Fixes # (issue)
6 |
7 | ## Type of change
8 |
9 | Please delete options that are not relevant.
10 |
11 | - [ ] Bug fix (non-breaking change which fixes an issue)
12 | - [ ] New feature (non-breaking change which adds functionality)
13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
14 | - [ ] This change requires a documentation update
15 |
16 | # How Has This Been Tested?
17 |
18 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
19 |
20 | - [ ] Test A
21 | - [ ] Test B
22 |
23 | **Test Configuration**:
24 |
25 |
26 | # Checklist:
27 |
28 | - [ ] My code follows the style guidelines of this project
29 | - [ ] I have performed a self-review of my own code
30 | - [ ] I have commented my code, particularly in hard-to-understand areas
31 | - [ ] I have made corresponding changes to the documentation
32 | - [ ] My changes generate no new warnings
33 | - [ ] I have added tests that prove my fix is effective or that my feature works
34 | - [ ] New and existing unit tests pass locally with my changes
35 | - [ ] Any dependent changes have been merged and published in downstream modules
36 |
--------------------------------------------------------------------------------
/docs/UI-HISTORY.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 |
5 | ## user interface
6 | This page shows how the UI of **media-dupes** changed over the time
7 |
8 | ### 0.1.0
9 | 
10 |
11 | ### 0.2.0
12 | 
13 |
14 | ### 0.3.0
15 | 
16 |
17 | ### 0.4.0
18 | 
19 |
20 | ### 0.5.0
21 | 
22 |
23 | ### 0.6.0
24 | 
25 |
26 | ### 0.7.0
27 | 
28 |
29 | ### 0.8.0
30 | 
31 |
32 | ### 0.9.0
33 | 
34 |
35 | ### 0.10.x
36 | 
37 |
--------------------------------------------------------------------------------
/app/js/modules/unhandled.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file unhandled.js
3 | * @fileOverview This module contains all unhandled functions, See: https://github.com/sindresorhus/electron-unhandled
4 | * @author yafp
5 | * @module unhandled
6 | */
7 |
8 | 'use strict'
9 |
10 | // ----------------------------------------------------------------------------
11 | // REQUIRE MODULES
12 | // ----------------------------------------------------------------------------
13 | //
14 | const { openNewGitHubIssue, debugInfo } = require('electron-util')
15 | const unhandled = require('electron-unhandled')
16 | unhandled({
17 | showDialog: true,
18 | reportButton: error => {
19 | openNewGitHubIssue({
20 | user: 'yafp',
21 | repo: 'media-dupes',
22 | body: `\`\`\`\n${error.stack}\n\`\`\`\n\n---\n\n${debugInfo()}`
23 | })
24 | }
25 | })
26 |
27 | /**
28 | * @function initUnhandled
29 | * @summary Initializes electron-unhandled
30 | * @description Initializes electron-unhandled. Is used in main and renderer
31 | */
32 | function initUnhandled () {
33 | const unhandled = require('electron-unhandled')
34 | const { openNewGitHubIssue, debugInfo } = require('electron-util')
35 |
36 | unhandled({
37 | showDialog: true,
38 | reportButton: error => {
39 | openNewGitHubIssue({
40 | user: 'yafp',
41 | repo: 'media-dupes',
42 | body: `\`\`\`\n${error.stack}\n\`\`\`\n\n---\n\n${debugInfo()}`
43 | })
44 | }
45 | })
46 | }
47 |
48 | // ----------------------------------------------------------------------------
49 | // EXPORT THE MODULE FUNCTIONS
50 | // ----------------------------------------------------------------------------
51 | //
52 | module.exports.initUnhandled = initUnhandled
53 |
--------------------------------------------------------------------------------
/jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "plugins/markdown",
4 | "plugins/summarize"
5 | ],
6 | "recurseDepth": 10,
7 | "source": {
8 | "include": [ "app/js/", "main.js" ],
9 | "exclude": [ ],
10 | "includePattern": ".+\\.js(doc|x)?$",
11 | "excludePattern": "(^|\\/|\\\\)_"
12 | },
13 | "sourceType": "module",
14 | "tags": {
15 | "allowUnknownTags": true,
16 | "dictionaries": ["jsdoc","closure"]
17 | },
18 | "templates": {
19 | "cleverLinks": false,
20 | "monospaceLinks": true
21 | },
22 | "opts": {
23 | "template": "node_modules/docdash",
24 | "encoding": "utf8",
25 | "destination": "./docs/jsdocs",
26 | "recurse": true,
27 | "verbose": true
28 | },
29 | "docdash": {
30 | "static": [true],
31 | "sort": [true],
32 | "sectionOrder": [
33 | "Classes",
34 | "Modules",
35 | "Externals",
36 | "Events",
37 | "Namespaces",
38 | "Mixins",
39 | "Tutorials",
40 | "Interfaces"
41 | ],
42 | "meta": {
43 | "title": "media-dupes jsdoc documentation",
44 | "description": "a simple JSDoc based documentation for media-dupes",
45 | "keyword": "media-dupes"
46 | },
47 | "search": [true],
48 | "collapse": [false],
49 | "wrap": [true],
50 | "typedefs": [true],
51 | "navLevel": [10],
52 | "private": [true],
53 | "menu": {
54 | "Github": {
55 | "href":"https://github.com/yafp/media-dupes",
56 | "target":"_blank",
57 | "class":"menu-item",
58 | "id":"website_link"
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/docs/INSTALL.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 |
5 | ## installation instructions
6 |
7 | ###  linux
8 |
9 | #### .AppImage
10 | * Download the latest .AppImage
11 | * Copy the AppImage to the desired location
12 | * Make it executable: `chmod +x /path/to/local/media-dupes-file.AppImage`
13 | * Execute it
14 |
15 | #### .deb
16 | * Download the latest .deb
17 | * execute: `sudo dpkg -i /path/to/local/media-dupes-file.deb`
18 |
19 | #### .pacman
20 | * Download the latest .pacman
21 | * execute: `sudo pacman -U /path/to/local/media-dupes-file.pacman`
22 |
23 | #### .rpm
24 | * Download the latest .rpm
25 | * execute:
26 | * `sudo dnf localinstall /path/to/local/media-dupes-file.rpm` or
27 | * `sudo yum localinstall /path/to/local/media-dupes-file.rpm`
28 |
29 | #### .snap
30 | * Download the latest .snap
31 | * execute: `sudo snap install /path/to/local/media-dupes-file.snap`
32 |
33 | ###  macOS
34 | #### .dmg
35 | * Download the latest `.dmg`
36 | * Mount the .dmg
37 | * Drag the .app to `/Applications`
38 |
39 | Informations about how to install non-app-store applications on macOS can be found [here](https://apple.stackexchange.com/questions/294013/how-to-allow-install-of-non-app-store-or-identified-developers-on-macos-sierra).
40 |
41 | ###  windows
42 |
43 | It's recommended to use the installer and use it just for the current user. This ensures that updating the bundles youtube-dl works out of the box.
44 |
45 | #### installer
46 | * Download the latest `win-installer.exe`
47 | * Execute the `win-installer.exe`
48 |
--------------------------------------------------------------------------------
/resources/installer/base_material/app_icon/Circle-icons-download.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.github/workflows/electron_builder.yml:
--------------------------------------------------------------------------------
1 | # ----------------------------------------------------------------------------
2 | # This action / workflow:
3 | # - executed on: macOS, linux and windows
4 | # - checksout the source
5 | # - installs some requirements
6 | # - installs some more requirements on linux
7 | # - builds using electron-builder
8 | # - releases if properly tagged
9 | #
10 | # Using: https://github.com/marketplace/actions/electron-builder-action
11 | # ----------------------------------------------------------------------------
12 |
13 | name: electron_builder
14 |
15 | on: push
16 |
17 | jobs:
18 | release:
19 | runs-on: ${{ matrix.os }}
20 |
21 | strategy:
22 | matrix:
23 | os: [macos-latest, ubuntu-latest, windows-latest]
24 |
25 | steps:
26 | - name: Check out Git repository
27 | uses: actions/checkout@v1
28 |
29 | - name: Install Node.js, NPM and Yarn
30 | uses: actions/setup-node@v1
31 | with:
32 | node-version: 10
33 |
34 | - name: apt-update
35 | if: startsWith(matrix.os, 'ubuntu-latest')
36 | run: sudo apt-get update
37 |
38 | - name: autoremove
39 | if: startsWith(matrix.os, 'ubuntu-latest')
40 | run: sudo apt autoremove
41 |
42 | - name: Install bsdtar rpm on Linux
43 | if: startsWith(matrix.os, 'ubuntu-latest')
44 | run: sudo apt-get install bsdtar rpm
45 |
46 | - name: Install snapcraft
47 | if: startsWith(matrix.os, 'ubuntu-latest')
48 | run: sudo snap install snapcraft --classic
49 |
50 | - name: Build/release Electron app
51 | uses: samuelmeuli/action-electron-builder@v1
52 | with:
53 | # GitHub token, automatically provided to the action
54 | # (No need to define this secret in the repo settings)
55 | github_token: ${{ secrets.github_token }}
56 |
57 | # If the commit is tagged with a version (e.g. "v1.0.0"),
58 | # release the app after building
59 | release: ${{ startsWith(github.ref, 'refs/tags/v') }}
60 |
--------------------------------------------------------------------------------
/app/js/modules/sentry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Contains all sentry functions
3 | * @author yafp
4 | * @module sentry
5 | */
6 | 'use strict'
7 |
8 | const utils = require('./utils.js')
9 |
10 | // ----------------------------------------------------------------------------
11 | // Error Handling using: sentry
12 | // ----------------------------------------------------------------------------
13 | //
14 | // https://sentry.io/organizations/yafp/
15 | // https://docs.sentry.io/platforms/javascript/electron/
16 | // https://docs.sentry.io/error-reporting/configuration/?platform=electron
17 | //
18 | const Sentry = require('@sentry/electron')
19 | Sentry.init({
20 | dsn: 'https://4bd3f512a1e34e24ab9838b00f57d131@sentry.io/1847606'
21 | // debug: true
22 | })
23 | //
24 | // simple way to force a crash:
25 | // myUndefinedFunctionInSentryModul();
26 |
27 | /**
28 | * @function enableSentry
29 | * @summary Enables sentry
30 | * @description Enabled sentry to log errors
31 | */
32 | function enableSentry () {
33 | Sentry.getCurrentHub().getClient().getOptions().enabled = true
34 | // utils.writeConsoleMsg('info', 'enableSentry ::: Enabled sentry')
35 | }
36 |
37 | /**
38 | * @function disableSentry
39 | * @summary Disables sentry
40 | * @description Disables sentry to log errors
41 | */
42 | function disableSentry () {
43 | Sentry.getCurrentHub().getClient().getOptions().enabled = false
44 | // utils.writeConsoleMsg('warn', 'disableSentry ::: Disabled sentry')
45 | }
46 |
47 | /**
48 | * @function countEvent
49 | * @summary Captures a message or event
50 | * @description Captures a message or event
51 | */
52 | function countEvent (eventName) {
53 | var isReportingEnabled = utils.globalObjectGet('enableErrorReporting')
54 | if (isReportingEnabled === true) {
55 | Sentry.captureMessage(eventName)
56 | }
57 | }
58 |
59 | // ----------------------------------------------------------------------------
60 | // EXPORT THE MODULE FUNCTIONS
61 | // ----------------------------------------------------------------------------
62 | //
63 | module.exports.enableSentry = enableSentry
64 | module.exports.disableSentry = disableSentry
65 | module.exports.countEvent = countEvent
66 |
--------------------------------------------------------------------------------
/test/spec.js:
--------------------------------------------------------------------------------
1 | console.log('Executing: test/spec.js')
2 |
3 | const Application = require('spectron').Application
4 | const assert = require('assert')
5 | const electronPath = require('electron') // Require Electron from the binaries included in node_modules.
6 | const path = require('path')
7 |
8 | // Load chai assertions
9 | const chaiAsPromised = require('chai-as-promised')
10 | const chai = require('chai')
11 | chai.should()
12 | chai.use(chaiAsPromised)
13 |
14 | var expect = chai.expect
15 |
16 | describe('Application Window', function () {
17 | this.timeout(20000)
18 |
19 | after(function () {
20 | if (this.app && this.app.isRunning()) {
21 | return this.app.stop()
22 | }
23 | })
24 |
25 | beforeEach(function () {
26 | this.app = new Application({
27 | // Your electron path can be any binary
28 | // i.e for OSX an example path could be '/Applications/MyApp.app/Contents/MacOS/MyApp'
29 | // But for the sake of the example we fetch it from our node_modules.
30 | path: electronPath,
31 |
32 | // The following line tells spectron to look and use the main.js file
33 | // and the package.json located 1 level above.
34 | args: [path.join(__dirname, '..')]
35 | })
36 | return this.app.start()
37 | })
38 |
39 | afterEach(function () {
40 | if (this.app && this.app.isRunning()) {
41 | return this.app.stop()
42 | }
43 | })
44 |
45 | // TEST: Check launching the app window
46 | //
47 | /*
48 | it('Open application window', function () {
49 | // this.timeout(10000);
50 | wrapper.find('input').simulate('keypress', {key: 'Enter'})
51 |
52 | // at least 1 window should be counted
53 | return this.app.client.getWindowCount().should.not.equal(0)
54 | })
55 | */
56 |
57 | // TEST: Check the window title
58 | //
59 | /*
60 | it('Check window title', function () {
61 | return this.app.client.browserWindow.getTitle().then(function (title) {
62 | expect(title).to.contain('media-dupes')
63 | return Promise.resolve()
64 | })
65 | })
66 | */
67 | })
68 |
--------------------------------------------------------------------------------
/app/distraction.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Start level:
46 |
47 | Play/Pause
48 | Quit
49 |
50 |
51 |
52 | Drop = Space
53 | Pause = ESC or P or Return
54 | Movement = Arrow Keys or Mouse or MouseWheel
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/docs/SENTRY.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 |
5 | ## General
6 | **media-dupes** is using [Sentry](https://sentry.io/for/javascript/) by default to:
7 |
8 | * count events
9 | * track errors
10 |
11 | This page tries to explain as detailed as possible what is tracked and why.
12 |
13 | **Please keep in mind:** *Error reporting and click counting* can be disabled in the application settings at any time.
14 |
15 | ## Use cases
16 | ### Event counting
17 | **media-dupes** is simply counting how often specific events happen. Those counts are overall counters for one particular events and counts for all users and therefor not user-specific.
18 |
19 | For example how often the Intro button was pressed.
20 |
21 | The following screenshot shows the related informations:
22 |
23 | 
24 |
25 | The following event get counted:
26 |
27 | * Main User Interface
28 | * Video button pressed
29 | * Audio button pressed
30 | * Overall application starts
31 | * Overall urls handled
32 | * Overall urls failed
33 | * Settings User Interface
34 | * Disabling error reporting in the settings UI
35 |
36 | ### Error reporting
37 | *Error reporting* heavily helps finding bugs in the source code. Right now **media-dupes** is developed only by a single person on a single linux computer. I am not able to run **media-dupes** on Windows or MacOS myself and even on Linux i will never cause and find all bugs myself. Error reports allow me to see what errors happen on might give me a starting point to try to fix them.
38 |
39 |
40 | ## Data
41 | ### What data is stored?
42 | Whenever either an *error* or one of the *event-count* actions is triggered sentry gets informations about
43 |
44 | * `browser.name`
45 | * `chrome`
46 | * `chrome.name`
47 | * `device.family`
48 | * `environment`
49 | * `event_type`
50 | * `level`
51 | * `node`
52 | * `node.name`
53 | * `os_name`
54 | * `release`
55 | * `runtime`
56 | * `runtime.name`
57 |
58 | The following screenshot shows those informations:
59 |
60 | 
61 |
62 | ### What data is not stored?
63 |
64 | * no ip addresses
65 |
66 | ## Questions
67 | Still got questions - feel free to contact me.
68 |
--------------------------------------------------------------------------------
/app/js/modules/urls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Contains all urls
3 | * @author yafp
4 | * @module urls
5 | */
6 | 'use strict'
7 |
8 | // ----------------------------------------------------------------------------
9 | // DEFINE URL CONSTANTS
10 | // ----------------------------------------------------------------------------
11 | //
12 |
13 | /**
14 | * @constant
15 | * @type {string}
16 | * @default
17 | */
18 | const urlGitHubGeneral = 'https://github.com/yafp/media-dupes' // repo url
19 |
20 | /**
21 | * @constant
22 | * @type {string}
23 | * @default
24 | */
25 | const urlGitHubIssues = 'https://github.com/yafp/media-dupes/issues' // issue url
26 |
27 | /**
28 | * @constant
29 | * @type {string}
30 | * @default
31 | */
32 | const urlGitHubChangelog = 'https://github.com/yafp/media-dupes/blob/master/docs/CHANGELOG.md' // changelog url
33 |
34 | /**
35 | * @constant
36 | * @type {string}
37 | * @default
38 | */
39 | const urlGitHubReleases = 'https://github.com/yafp/media-dupes/releases' // releases url
40 |
41 | /**
42 | * @constant
43 | * @type {string}
44 | * @default
45 | */
46 | const urlGitHubRepoTags = 'https://api.github.com/repos/yafp/media-dupes/tags' // tags api url
47 |
48 | /**
49 | * @constant
50 | * @type {string}
51 | * @default
52 | */
53 | const urlGithubApiReleases = 'https://api.github.com/repos/yafp/media-dupes/releases' // release api
54 |
55 | /**
56 | * @constant
57 | * @type {string}
58 | * @default
59 | */
60 | const urlGithubSentryUsage = 'https://github.com/yafp/media-dupes/blob/master/docs/SENTRY.md' // sentry usage
61 |
62 | /**
63 | * @constant
64 | * @type {string}
65 | * @default
66 | */
67 | const urlYoutubeDlSupportedSites = 'https://ytdl-org.github.io/youtube-dl/supportedsites.html' // supported sites by youtube-dl
68 |
69 | // ----------------------------------------------------------------------------
70 | // EXPORT THE MODULE FUNCTIONS
71 | // ----------------------------------------------------------------------------
72 | //
73 | module.exports.urlGitHubGeneral = urlGitHubGeneral
74 | module.exports.urlGitHubIssues = urlGitHubIssues
75 | module.exports.urlGitHubChangelog = urlGitHubChangelog
76 | module.exports.urlGitHubReleases = urlGitHubReleases
77 | module.exports.urlGitHubRepoTags = urlGitHubRepoTags
78 | module.exports.urlGithubApiReleases = urlGithubApiReleases
79 | module.exports.urlGithubSentryUsage = urlGithubSentryUsage
80 | module.exports.urlYoutubeDlSupportedSites = urlYoutubeDlSupportedSites
81 |
--------------------------------------------------------------------------------
/docs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | # media-dupes
5 | ## Contributing
6 |
7 | ### general
8 | When contributing to this repository, please first discuss the change you wish to make via issue,
9 | email, or any other method with the owners of this repository before making a change.
10 |
11 | ### jsdoc
12 | The current jsdoc documentation can be found on [https://yafp.github.io/media-dupes/](https://yafp.github.io/media-dupes/). It is auto-generated (using [JsDoc Action](https://github.com/marketplace/actions/jsdoc-action)) on each commit of this project.
13 |
14 |
15 | ### Building media-dupes
16 | Building **media-dupes** yourself is pretty easy. Please see the [building instructions](BUILD.md) for more details.
17 |
18 |
19 | ### Sentry (Crashreports)
20 | Crashreport informations are located [here](https://sentry.io/organizations/yafp/issues/?project=1847606).
21 |
22 |
23 |
24 | ### Getting started
25 |
26 | #### 1. First steps
27 |
28 | ##### Clone repo
29 | * Clone the repository: `git clone https://github.com/yafp/media-dupes`
30 |
31 | ##### Install dependencies
32 | * Go into the repository: `cd media-dupes`
33 | * Install dependencies: `npm install`
34 |
35 | ##### Run the code
36 | * Execute: `npm start`
37 |
38 | ##### Run the code with debug logging
39 | * Execute `npm run start-debug`
40 |
41 | ##### Run some basic test
42 | * Execute: `npm test`
43 |
44 |
45 |
46 | #### 2. Misc howto's
47 |
48 | ##### Auditing
49 |
50 | ###### npm auditing (scan for vulnerabilities)
51 | * `npm audit`
52 |
53 | ##### Install packages
54 |
55 | ###### install single package
56 | * `npm install PACKAGENAME --save`
57 |
58 | ###### npm: install single package in specific version
59 | * `npm install PACKAGENAME@1.2.3`
60 |
61 |
62 | ##### Outdated packages
63 |
64 | ###### check for outdated npm packages
65 | * `npm outdated`
66 |
67 | or using `npm-check`:
68 |
69 | * Install requirements: `npm install -g npm-check`
70 | * Run:
71 | * `npm-check` or
72 | * `npm-check -s` (-s = Skip check for unused packages)
73 |
74 |
75 | ##### Updating
76 |
77 | ###### check dependencies
78 | * Install requirements: `npm install depcheck`
79 | * Run check: `depcheck`
80 |
81 | ###### update single package
82 | * `npm install PACKAGENAME --save`
83 |
84 | ###### update all packages
85 | * `npm update`
86 |
87 |
88 | ##### Others
89 | ###### List all package.json scripts
90 | * `npm run`
91 |
92 | or a dynamic solution
93 |
94 | * Install requirements: `npm i -g ntl`
95 | * Execute `ntl`
96 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at fidel@yafp.de. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
media-dupes
7 |
8 | a minimal content duplicator for common media services like youtube
9 |
10 | available for:
11 |
12 | 
13 | 
14 | 
15 |
16 | [](https://deepscan.io/dashboard#view=project&tid=8831&pid=11041&bid=160247)
17 | [](https://www.codacy.com/manual/yafp/media-dupes?utm_source=github.com&utm_medium=referral&utm_content=yafp/media-dupes&utm_campaign=Badge_Grade)
18 | 
19 | 
20 | 
21 | 
22 | 
23 | 
24 | 
25 | [](https://github.com/yafp/media-dupes/graphs/contributors/)
26 | [](https://github.com/yafp/media-dupes/pulls?q=is:pr+is:merged)
27 | [](https://github.com/yafp/media-dupes/stargazers)
28 | 
29 | [](https://yafp.github.io/media-dupes/)
30 | 
31 |
32 |
33 | 
34 |
35 |
36 |
37 | # project is discontinued
38 |
39 | # about
40 | **media-dupes** can[1](#footnote1) :
41 |
42 | * download video
43 | * download audio
44 |
45 | **media-dupes** is:
46 |
47 | * based on `electron`
48 | * using bundled versions of `youtube-dl` and `ffmpeg` to do it's magic
49 | * available for *freebsd*, *linux*, *macOS* and *windows*.
50 | * free and open source
51 |
52 | # getting started
53 |
54 | ### download
55 | You'll find the latest releases [here](https://github.com/yafp/media-dupes/releases).
56 |
57 | ### install
58 | Please see the [installation instructions](docs/INSTALL.md) for more details.
59 |
60 | ### updates
61 | **media-dupes** checks on application launch if there is a newer `media-dupes` version available.
62 | Updates must be installed manually as automatic updating of electron applications requires that the builds are code-signed, which i can't provide so far.
63 |
64 | **media-dupes** checks as well on application launch if there are updates available for the bundled `youtube-dl` tool.
65 |
66 | **Be aware:**
67 |
68 | Updating the bundled `youtube-dl` package is only possible if the user has write access to the `media-dupes` installation path (which does not work for all available packages/installation types).
69 |
70 | ### changelog
71 | Please see the [changelog](docs/CHANGELOG.md) for more details.
72 |
73 | ### license
74 | Please see the [LICENSE](LICENSE) for more details.
75 |
76 | ### debugging / verbose / logging
77 | Please see the [verbose](docs/VERBOSE.md) informations for more details.
78 |
79 | ### privacy
80 | * **media-dupes** is using sentry to collect error reports and do some anonymous usage stats. This helps heavily finding bugs which might occur only in some specific use-cases.
81 | * reporting is enabled by default, but can be disabled in the application settings UI.
82 | * media-dupes is not tracking it's users (i.e. using Google Analytics or similar)
83 | * no ip addresses are stored
84 |
85 | Please check [here](docs/SENTRY.md) how **media-dupes** is using sentry and why.
86 |
87 | ### discussion
88 | If you have question regarding **media-dupes** click [here](https://github.com/yafp/media-dupes/issues) to create an issue
89 |
90 | ### support / fund
91 | If you want to support the development of **media-dupes** you can fund me on:
92 |
93 | * [github](https://github.com/sponsors/yafp)
94 | * [patreon](https://www.patreon.com/yafp)
95 |
96 | ### disclosure
97 | **media-dupes** is not affiliated with any of the supported apps/services.
98 |
99 |
100 | # developers
101 | You are always welcome to check and even improve the code.
102 |
103 | * Please see the [contributing informations](docs/CONTRIBUTING.md) for more details.
104 | * A list of all contributors can be found [here](docs/CONTRIBUTORS.md).
105 |
106 |
107 | ***
108 |
109 | ###### footnotes
110 | 1 : Assuming the provided url is supported by [youtube-dl](https://ytdl-org.github.io/youtube-dl/supportedsites.html).
111 |
--------------------------------------------------------------------------------
/app/css/core.css:
--------------------------------------------------------------------------------
1 | /*
2 | bootstrap color codes: https://www.bitdegree.org/learn/bootstrap-colors
3 | primary: #007bff
4 | info: #17a2b8
5 | success: #28a745
6 | warning: #ffc107
7 | danger: #dc3545
8 | dark: #343a40
9 | secondary #6c757d
10 | light #f8f9fa
11 | white #ffffff
12 | */
13 |
14 | /* ########################################################################## */
15 | /* FONT */
16 | /* ########################################################################## */
17 | @font-face {
18 | font-family: 'Arial';
19 | src: url('../fonts/Arial/Arial.ttf') format('opentype');
20 | font-family: 'ArialMono';
21 | src: url('../fonts/ArialMono/Arial_Monospaced_MT.ttf') format('opentype');
22 | }
23 |
24 | /* ########################################################################## */
25 | /* BODY */
26 | /* ########################################################################## */
27 | body {
28 | font-family: 'Arial';
29 | }
30 |
31 | /* ########################################################################## */
32 | /* BOOTSTRAP MODAL - used as SPLASH */
33 | /* ########################################################################## */
34 | /* background color of the modal */
35 | .modal-backdrop {
36 | opacity: 0.0 !important;
37 | }
38 |
39 | /* removing border from splash */
40 | .no-border {
41 | border: none !important;
42 | }
43 |
44 | /* ########################################################################## */
45 | /* THE MAIN UI WINDOW CONTAINER */
46 | /* ########################################################################## */
47 | #mainContainer {
48 | animation-delay: 0.5s; /* custom animation delay */
49 | }
50 |
51 | /* ########################################################################## */
52 | /* WINDOW BORDER */
53 | /* ########################################################################## */
54 | .container-after-titlebar {
55 | border-left: 1px solid #343a40;
56 | border-right: 1px solid #343a40;
57 | border-bottom: 1px solid #343a40;
58 | }
59 |
60 | /* ########################################################################## */
61 | /* UI TEXT - MAKE THEM NOT SELECTABLE */
62 | /* ########################################################################## */
63 | .mediaDupes_nonSelectableText {
64 | -webkit-user-select:none;
65 | -moz-user-select: none;
66 | }
67 |
68 | /* ########################################################################## */
69 | /* CUSTOM-TITLEBAR */
70 | /* ########################################################################## */
71 | .window-title {
72 | font-size: 13px !important;
73 | }
74 |
75 | /* ########################################################################## */
76 | /* HEADINGS */
77 | /* ########################################################################## */
78 | h2 {
79 | text-align: right;
80 | color: gray;
81 | font-size: 24px;
82 | }
83 |
84 | h3 {
85 | text-align: right;
86 | color: gray;
87 | font-size: 14px;
88 | }
89 |
90 | h4 {
91 | color: #c0392b;
92 | }
93 |
94 | h5 {
95 | color: gray;
96 | font-size: 18px;
97 | }
98 |
99 | h6 {
100 | font-size: 14px;
101 | color: gray;
102 | }
103 |
104 | /* ########################################################################## */
105 | /* LOG TEXTAREA */
106 | /* ########################################################################## */
107 | textarea {
108 | font-size: 12px !important;
109 | font-weight: bold;
110 | font-family: 'ArialMono';
111 |
112 | background-color: #ecf0f1;
113 | background-repeat: no-repeat;
114 | background-position: center;
115 | width: 32px;
116 | height:32px;
117 | }
118 |
119 | #textareaLogOutput {
120 | background-image: url("../img/background/icon_textarea_log_64x64.png");
121 | min-width: 100%;
122 | width: 100%;
123 | min-height: 200px;
124 | }
125 |
126 | /* ########################################################################## */
127 | /* DATATABLES */
128 | /* ########################################################################## */
129 | /* table head */
130 | th {
131 | font-size: 12px;
132 | color: gray;
133 | }
134 |
135 | /* table rows */
136 | td {
137 | font-size: 11px;
138 | color: gray;
139 | }
140 |
141 | /* hover color for rows */
142 | table#example.dataTable tbody tr:hover {
143 | background-color: #ffe5e5;
144 | }
145 |
146 | table#example.dataTable tbody tr:hover > .sorting_1 {
147 | background-color: #ffe5e5;
148 | }
149 |
150 | .zoomSmall:hover {
151 | transform: scale(1.5);
152 | }
153 |
154 | .zoomBig:hover {
155 | transform: scale(5.0);
156 | }
157 |
158 | #todoList {
159 | height: 200px;
160 | }
161 |
162 | /* ########################################################################## */
163 | /* LINKs */
164 | /* ########################################################################## */
165 | /* unvisited link */
166 | .classicLink {
167 | color: #c0392b;
168 | }
169 |
170 | .classicLink:hover {
171 | color: #c0392b;
172 | }
173 |
174 | /* ########################################################################## */
175 | /* ROW EMPTY */
176 | /* ########################################################################## */
177 | .row-empty {
178 | height: 15px;
179 | }
180 |
181 | /* ########################################################################## */
182 | /* BUTTONS */
183 | /* ########################################################################## */
184 | .mediaDupes_btnDefaultWidth {
185 | min-width: 100px;
186 | }
187 |
188 | .mediaDupes_btnSmallWidth {
189 | min-width: 50px;
190 | }
191 |
192 | .mediaDupes_btnDownloadActionWidth {
193 | min-width: 160px;
194 | }
195 |
196 | /* ########################################################################## */
197 | /* APPLICATION STATE */
198 | /* ########################################################################## */
199 | #applicationState {
200 | color: gray;
201 | margin: 0 auto;
202 | width: 400px;
203 | min-width: 400px;
204 | text-align: center;
205 | font-size: 12px;
206 | }
207 |
208 | /* ########################################################################## */
209 | /* BOTTOM */
210 | /* ########################################################################## */
211 | .applicationWindowBottom {
212 | margin-left: 15px;
213 | margin-right: 15px;
214 | }
215 |
216 | /* ########################################################################## */
217 | /* FADEIN THE WINDOW */
218 | /* ########################################################################## */
219 | @keyframes fadein {
220 | from {
221 | opacity: 0;
222 | }
223 | to {
224 | opacity: 1;
225 | }
226 | }
227 |
228 | .app {
229 | /* Disable text selection, or your app will feel like a web page */
230 | -webkit-user-select: none;
231 | -webkit-app-region: drag;
232 |
233 | /* Cover the whole window */
234 | /* height: 100%; */
235 |
236 | /* Make sure this matches the native window background color that you pass to
237 | * electron.BrowserWindow({...}), otherwise your app startup will look janky. */
238 | background: rgb(255, 255, 255);
239 |
240 | /* Smoother startup */
241 | animation: fadein 0.5s;
242 | }
243 |
244 | /* ########################################################################## */
245 | /* INTRO.JS - Fixing issues where the UI element was just white - not visible */
246 | /* see: https://github.com/usablica/intro.js/issues/840 */
247 | /* ########################################################################## */
248 | .introjs-helperLayer {
249 | background: transparent !important;
250 | box-shadow: 0 0 0 9999px
251 | rgba(1, 1, 1, 0.8);
252 | }
253 |
254 | .introjs-overlay {
255 | background: transparent !important;
256 | }
257 |
258 | /* ########################################################################## */
259 | /* FIXING THE HORIZONTAL SCROLLBAR WHICH APPEARS ON i.e. ARCH LINUX on first load */
260 | /* ########################################################################## */
261 | ::-webkit-scrollbar {
262 | display: none;
263 | }
264 |
265 | /* ########################################################################## */
266 | /* SETTINGS: NAV COLOR */
267 | /* ########################################################################## */
268 | .nav-link{
269 | color: gray;
270 | }
271 |
272 | .nav-link:hover {
273 | color: #c0392b;
274 | }
275 |
276 | .nav-item.nav-link.active {
277 | color: #c0392b;
278 | }
279 |
280 | /* ########################################################################## */
281 | /* PLAYGROUND: Background colors for development */
282 | /* ########################################################################## */
283 | .bg_yellow {
284 | background-color: #F0E68C;
285 | }
286 |
287 | .bg_red {
288 | background-color: #FA8072;
289 | }
290 |
291 | .bg_blue {
292 | background-color: #87CEEB;
293 | }
294 |
295 | .bg_green {
296 | background-color: #90EE90;
297 | }
298 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "media-dupes",
3 | "productName": "media-dupes",
4 | "desktopName": "media-dupes.desktop",
5 | "version": "0.10.3",
6 | "description": "a minimal content duplicator for common media services like youtube",
7 | "main": "main.js",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/yafp/media-dupes"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/yafp/media-dupes/issues"
14 | },
15 | "contributors": [
16 | {
17 | "name": "Florian Pöck",
18 | "email": "info@yafp.de",
19 | "url": "https://yafp.de"
20 | }
21 | ],
22 | "homepage": "https://github.com/yafp/media-dupes",
23 | "keywords": [
24 | "aac",
25 | "audio",
26 | "download",
27 | "downloader",
28 | "dupes",
29 | "duplicator",
30 | "extractor",
31 | "ffmpeg",
32 | "flac",
33 | "m4a",
34 | "media",
35 | "media-dupes",
36 | "mediathek",
37 | "mixcloud.com",
38 | "mp3",
39 | "mirror",
40 | "ogg",
41 | "opus",
42 | "soundcloud",
43 | "stream",
44 | "video",
45 | "vorbis",
46 | "wav",
47 | "youtube",
48 | "youtube-dl"
49 | ],
50 | "author": "yafp ",
51 | "license": "GPL-3.0",
52 | "standardx": {
53 | "ignore": [
54 | "/dist",
55 | "/docs/jsdocs",
56 | "/test/spec.js"
57 | ]
58 | },
59 | "scripts": {
60 | "start": "electron .",
61 | "start-debug": "electron . --enable-logging",
62 | "start-verbose": "electron . --verbose",
63 | "show-help": "electron . --help",
64 | "show-version": "electron . --version",
65 | "clean": "rm -rf node_modules",
66 | "clean-dist": "rimraf dist/*",
67 | "clean-modules": "rimraf 'node_modules/!(rimraf|.bin)'",
68 | "reinstall": "npm run clean && npm install",
69 | "jsdocs-delete": "rimraf docs/jsdocs/*",
70 | "jsdocs-create": "./node_modules/jsdoc/jsdoc.js --configure jsdoc.json --readme README.md",
71 | "jsdocs-update": "npm run jsdocs-delete && npm run jsdocs-create",
72 | "standardx": "standardx --verbose | snazzy",
73 | "standardx-fix": "standardx --fix --verbose | snazzy",
74 | "test": "mocha",
75 | "lint": "node_modules/eslint/bin/eslint.js app/js --ignore-path=.eslintignore",
76 | "pack-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --icon=resources/installer/icon.icns --prune=true --out=dist",
77 | "pack-win": "npm run pack-win-32 && npm run pack-win-64",
78 | "pack-win-32": "electron-packager . media-dupes --overwrite --platform=win32 --arch=ia32 --icon=resources/installer/icon.ico --prune=true --out=dist --version-string.CompanyName=yafp --version-string.FileDescription=${productName} --version-string.ProductName=\"media-dupes\"",
79 | "pack-win-64": "electron-packager . media-dupes --overwrite --platform=win32 --arch=x64 --icon=resources/installer/icon.ico --prune=true --out=dist --version-string.CompanyName=yafp --version-string.FileDescription=${productName} --version-string.ProductName=\"media-dupes\"",
80 | "pack-linux": "electron-packager . media-dupes --overwrite --platform=linux --arch=x64 --icon=resources/installer/icons/1024x1024.png --prune=true --out=dist",
81 | "build-mac": "electron-builder --mac --publish=onTagOrDraft",
82 | "build-win": "electron-builder --win --ia32 --x64 --publish=onTagOrDraft",
83 | "build-linux": "electron-builder --linux --publish=onTagOrDraft"
84 | },
85 | "build": {
86 | "productName": "media-dupes",
87 | "appId": "de.yafp.media-dupes",
88 | "extraFiles": [
89 | "LICENSE",
90 | "README.md",
91 | "docs/CHANGELOG.md"
92 | ],
93 | "asar": false,
94 | "mac": {
95 | "category": "public.app-category.productivity",
96 | "files": [
97 | "!node_modules/ffmpeg-static-electron/bin/linux/*",
98 | "!node_modules/ffmpeg-static-electron/bin/win/*"
99 | ],
100 | "artifactName": "${productName}-${version}-mac.${ext}",
101 | "target": [
102 | "dmg"
103 | ]
104 | },
105 | "dmg": {
106 | "title": "media-dupes",
107 | "background": "resources/installer/dmg_background.png",
108 | "iconSize": 80,
109 | "iconTextSize": 12,
110 | "contents": [
111 | {
112 | "x": 300,
113 | "y": 250,
114 | "type": "link",
115 | "path": "/Applications"
116 | },
117 | {
118 | "x": 300,
119 | "y": 25,
120 | "type": "file"
121 | }
122 | ]
123 | },
124 | "win": {
125 | "icon": "resources/installer/icon.ico",
126 | "publisherName": "yafp",
127 | "files": [
128 | "!node_modules/ffmpeg-static-electron/bin/linux/*",
129 | "!node_modules/ffmpeg-static-electron/bin/mac/*"
130 | ],
131 | "artifactName": "${productName}-${version}-win-${arch}.${ext}",
132 | "target": [
133 | "zip",
134 | "nsis",
135 | "portable"
136 | ]
137 | },
138 | "portable": {
139 | "unicode": false,
140 | "artifactName": "${productName}-${version}-win-portable-${arch}.${ext}"
141 | },
142 | "nsis": {
143 | "include": "resources/installer/nsis/installer.nsh",
144 | "deleteAppDataOnUninstall": true,
145 | "unicode": false,
146 | "artifactName": "${productName}-${version}-win-installer-${arch}.${ext}",
147 | "createDesktopShortcut": true,
148 | "createStartMenuShortcut": true,
149 | "oneClick": false,
150 | "allowElevation": true,
151 | "allowToChangeInstallationDirectory": true,
152 | "runAfterFinish": false,
153 | "uninstallDisplayName": "${productName}-${version}"
154 | },
155 | "snap": {
156 | "publish": [
157 | {
158 | "provider": "github"
159 | }
160 | ]
161 | },
162 | "linux": {
163 | "executableName": "media-dupes",
164 | "files": [
165 | "!node_modules/ffmpeg-static-electron/bin/win/*",
166 | "!node_modules/ffmpeg-static-electron/bin/mac/*"
167 | ],
168 | "artifactName": "${productName}-${version}-linux-${arch}.${ext}",
169 | "description": "a minimal content duplicator for common media services like youtube",
170 | "category": "Network",
171 | "desktop": {
172 | "Terminal": "false",
173 | "Type": "Application",
174 | "Categories": "GTK;GNOME;Network;Utility;AudioVideo;"
175 | },
176 | "target": [
177 | {
178 | "target": "deb",
179 | "arch": [
180 | "x64"
181 | ]
182 | },
183 | {
184 | "target": "pacman",
185 | "arch": [
186 | "x64"
187 | ]
188 | },
189 | {
190 | "target": "zip",
191 | "arch": [
192 | "x64"
193 | ]
194 | },
195 | {
196 | "target": "AppImage",
197 | "arch": [
198 | "x64"
199 | ]
200 | },
201 | {
202 | "target": "snap",
203 | "arch": [
204 | "x64"
205 | ]
206 | },
207 | {
208 | "target": "freebsd",
209 | "arch": [
210 | "x64"
211 | ]
212 | },
213 | {
214 | "target": "rpm",
215 | "arch": [
216 | "x64"
217 | ]
218 | }
219 | ]
220 | },
221 | "directories": {
222 | "buildResources": "resources/installer/",
223 | "output": "dist/"
224 | },
225 | "publish": [
226 | {
227 | "provider": "github",
228 | "owner": "yafp",
229 | "repo": "media-dupes",
230 | "vPrefixedTagName": false
231 | }
232 | ]
233 | },
234 | "devDependencies": {
235 | "docdash": "^1.2.0",
236 | "electron": "^9.1.2",
237 | "electron-builder": "^22.8.0",
238 | "electron-packager": "^15.0.0",
239 | "eslint": "^7.6.0",
240 | "jsdoc": "^3.6.5",
241 | "ntl": "^5.1.0",
242 | "rimraf": "^3.0.2",
243 | "snazzy": "^8.0.0",
244 | "standardx": "^5.0.0"
245 | },
246 | "dependencies": {
247 | "bootstrap": "^4.4.1",
248 | "@fortawesome/fontawesome-free": "^5.14.0",
249 | "@sentry/electron": "^1.5.2",
250 | "about-window": "^1.13.4",
251 | "animate.css": "^3.7.2",
252 | "classic-tetris-js": "^1.0.2",
253 | "custom-electron-titlebar": "^3.2.3",
254 | "datatables.net-dt": "^1.10.21",
255 | "datatables.net-scroller-dt": "^2.0.2",
256 | "electron-json-storage": "^4.2.0",
257 | "electron-log": "^4.2.2",
258 | "electron-prompt": "^1.6.0",
259 | "electron-unhandled": "^3.0.2",
260 | "electron-util": "^0.14.2",
261 | "ffmpeg-static-electron": "^2.0.1",
262 | "got": "^11.5.1",
263 | "intro.js": "^2.9.3",
264 | "jquery": "^3.5.1",
265 | "md5": "^2.2.1",
266 | "metascraper": "^5.13.1",
267 | "metascraper-audio": "^5.13.1",
268 | "metascraper-description": "^5.13.1",
269 | "metascraper-image": "^5.13.1",
270 | "metascraper-logo": "^5.13.1",
271 | "metascraper-logo-favicon": "^5.13.1",
272 | "metascraper-media-provider": "^5.13.1",
273 | "metascraper-soundcloud": "^5.13.1",
274 | "metascraper-title": "^5.13.1",
275 | "metascraper-video": "^5.13.1",
276 | "metascraper-youtube": "^5.13.1",
277 | "noty": "^3.2.0-beta",
278 | "popper.js": "^1.16.1",
279 | "semver": "^7.3.2",
280 | "time-stamp": "^2.2.0",
281 | "v8-compile-cache": "^2.1.1",
282 | "yargs": "^15.4.1",
283 | "youtube-dl": "^3.0.2",
284 | "youtube-suggest": "^1.1.0"
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/app/js/modules/settings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Contains all settings functions
3 | * @author yafp
4 | * @module settings
5 | */
6 | 'use strict'
7 |
8 | // ----------------------------------------------------------------------------
9 | // REQUIRE MODULES
10 | // ----------------------------------------------------------------------------
11 | //
12 | const utils = require('./utils.js')
13 | const sentry = require('./sentry.js')
14 | const youtubeDl = require('./youtubeDl.js')
15 |
16 | /**
17 | * @function settingsFolderOpen
18 | * @summary Gets triggered from button on settings.html. Triggers code in main.js which opens the directory which contains possible user-settings-files
19 | * @description Gets triggered from button on settings.html. Triggers code in main.js which opens the directory which contains possible user-settings-files
20 | * @memberof renderer
21 | */
22 | function settingsFolderOpen () {
23 | utils.writeConsoleMsg('info', 'settingsFolderOpen ::: User wants to open the folder with user config files.')
24 | const { ipcRenderer } = require('electron')
25 | ipcRenderer.send('settingsFolderOpen')
26 | }
27 |
28 | /**
29 | * @function settingsOpenDevTools
30 | * @summary Tells the main process to open devTools for settings UI
31 | * @description Tells the main process to open devTools for settings UI
32 | */
33 | function settingsOpenDevTools () {
34 | const { ipcRenderer } = require('electron')
35 | ipcRenderer.send('settingsToggleDevTools')
36 | }
37 |
38 | /**
39 | * @function settingsSelectDownloadDir
40 | * @summary Let the user choose a custom download target directory
41 | * @description Is triggered via button on settings.html.
42 | */
43 | function settingsSelectDownloadDir () {
44 | const options = { properties: ['openDirectory'] }
45 | const { dialog } = require('electron').remote
46 |
47 | utils.writeConsoleMsg('info', 'settingsSelectDownloadDir ::: User wants to set a custom download directory. Now opening dialog to select a new download target')
48 | dialog.showOpenDialog(options).then(res => {
49 | utils.writeConsoleMsg('warn', '_' + res.filePaths + '_')
50 |
51 | if (res.filePaths.length === 0) {
52 | utils.writeConsoleMsg('warn', 'settingsSelectDownloadDir ::: User aborted selecting a custom download directory path in settings')
53 | utils.showNoty('info', 'No changes applied.')
54 | } else {
55 | var newDownloadDirectory = res.filePaths.toString()
56 | utils.userSettingWrite('downloadDir', newDownloadDirectory) // save the value to user-config
57 | $('#inputCustomTargetDir').val(newDownloadDirectory) // show it in the UI
58 | utils.writeConsoleMsg('info', 'settingsSelectDownloadDir ::: User selected the following directory: _' + newDownloadDirectory + '_ as download target.')
59 |
60 | // FIXME - is this needed?
61 | // utils.globalObjectSet('downloadDir', newDownloadDirectory)
62 | }
63 | })
64 | }
65 |
66 | /**
67 | * @function settingsToggleVerboseMode
68 | * @summary Enables or disabled the verbose mode
69 | * @description Enables or disabled the verbode mode
70 | */
71 | function settingsToggleVerboseMode () {
72 | if ($('#checkboxEnableVerbose').is(':checked')) {
73 | utils.writeConsoleMsg('info', 'settingsToggleVerboseMode ::: Verbose Mode is now enabled')
74 | utils.userSettingWrite('enableVerboseMode', true)
75 | } else {
76 | utils.writeConsoleMsg('info', 'settingsToggleVerboseMode ::: Verbose Mode is now disabled')
77 | utils.userSettingWrite('enableVerboseMode', false)
78 | }
79 | }
80 |
81 | /**
82 | * @function settingsToggleAdditionalParameter
83 | * @summary Enables or disabled the verbose mode
84 | * @description It is disabled by default. Enables or disabled the verbode mode
85 | */
86 | function settingsToggleAdditionalParameter () {
87 | if ($('#checkboxEnableAdditionalParameter').is(':checked')) {
88 | utils.writeConsoleMsg('info', 'settingsToggleAdditionalParameter ::: Additional parameter is now enabled')
89 | utils.userSettingWrite('enableAdditionalParameter', true)
90 | } else {
91 | utils.writeConsoleMsg('info', 'settingsToggleAdditionalParameter ::: Additional parameter is now disabled')
92 | utils.userSettingWrite('enableAdditionalParameter', false)
93 | }
94 | }
95 |
96 | /**
97 | * @function settingsSaveAdditionalParameter
98 | * @summary Saves the content of the input field for additional parameters
99 | * @description Saves the content of the input field for additional parameters
100 | */
101 | function settingsSaveAdditionalParameter () {
102 | const youtubeDlUnsupportedParameter = youtubeDl.blacklistedParameter // get the blacklisted parameters
103 |
104 | var inputContainsErrors = false // used to remember if an error occured
105 |
106 | var newAdditionalUserParameters = $('#textInputAdditionalParameter').val() // get value from UI
107 | var splittedParameters = newAdditionalUserParameters.split(' ') // split the input string into single elements
108 | if (splittedParameters.length > 0) { // If there are elements
109 | for (var j = 0; j < splittedParameters.length; j++) { // check for each element
110 | if ($.inArray(splittedParameters[j], youtubeDlUnsupportedParameter) > -1) { // if it is blacklistet
111 | utils.writeConsoleMsg('warn', 'settingsSaveAdditionalParameter ::: The additional parameter: _' + splittedParameters[j] + '_ is blacklisted.')
112 | utils.showNoty('error', 'The additional parameter ' + splittedParameters[j] + ' is blacklisted. Please remove it and try it again.', 0)
113 | inputContainsErrors = true
114 | } else { // or not
115 | utils.writeConsoleMsg('info', 'settingsSaveAdditionalParameter ::: The additional parameter: _' + splittedParameters[j] + '_ is not blacklisted.')
116 | }
117 | }
118 | }
119 |
120 | if (inputContainsErrors === false) {
121 | utils.writeConsoleMsg('info', 'settingsSaveAdditionalParameter ::: Saving additional parameters - new value is: _' + newAdditionalUserParameters + '_.')
122 | utils.userSettingWrite('additionalYoutubeDlParameter', newAdditionalUserParameters)
123 | }
124 | }
125 |
126 | /**
127 | * @function settingsTogglePrereleases
128 | * @summary Enables or disables the support for pre-releases
129 | * @description Enables or disables the setting to include pre-releases into the update -search
130 | */
131 | function settingsTogglePrereleases () {
132 | if ($('#checkboxEnablePreReleases').is(':checked')) {
133 | utils.writeConsoleMsg('info', 'settingsTogglePrereleases ::: Update-Search will now include pre-releases')
134 | utils.userSettingWrite('enablePrereleases', true)
135 | } else {
136 | utils.writeConsoleMsg('info', 'settingsTogglePrereleases ::: Update-Search will ignore pre-releases')
137 | utils.userSettingWrite('enablePrereleases', false)
138 | }
139 | }
140 |
141 | /**
142 | * @function settingsToggleErrorReporting
143 | * @summary Enables or disabled the error reporting function
144 | * @description Enables or disabled the error reporting function
145 | */
146 | function settingsToggleErrorReporting () {
147 | if ($('#checkboxEnableErrorReporting').is(':checked')) {
148 | utils.writeConsoleMsg('info', 'settingsToggleErrorReporting ::: Error reporting is now enabled')
149 | utils.userSettingWrite('enableErrorReporting', true)
150 | sentry.enableSentry()
151 | } else {
152 | // ask if user really wants to disable error-reporting (using a confirm dialog)
153 | const Noty = require('noty')
154 | var n = new Noty(
155 | {
156 | theme: 'bootstrap-v4',
157 | layout: 'bottom',
158 | type: 'info',
159 | closeWith: [''], // to prevent closing the confirm-dialog by clicking something other then a confirm-dialog-button
160 | text: 'Do you really want to disable reporting? * We don\'t track users * We don\'t store any IP informations * We only collect error reports and event counts This helps improving media-dupes',
161 | buttons: [
162 | Noty.button('Yes', 'btn btn-danger mediaDupes_btnDownloadActionWidth', function () {
163 | n.close()
164 | utils.writeConsoleMsg('info', 'settingsToggleErrorReporting ::: Error reporting is now disabled')
165 | utils.userSettingWrite('enableErrorReporting', false)
166 | sentry.disableSentry()
167 | sentry.countEvent('usageSettingsErrorReportingDisabled')
168 | // myUndefinedFunctionFromRendererAfterDisable()
169 | },
170 | {
171 | id: 'button1', 'data-status': 'ok'
172 | }),
173 |
174 | Noty.button('No', 'btn btn-success mediaDupes_btnDownloadActionWidth float-right', function () {
175 | n.close()
176 | $('#checkboxEnableErrorReporting').prop('checked', true) // revert state of checkbox
177 | utils.showNoty('success', 'Thanks for supporting media-dupes development with your error reports.')
178 | utils.writeConsoleMsg('info', 'settingsToggleErrorReporting ::: User cancelled disabling of error-reporting')
179 | })
180 | ]
181 | })
182 |
183 | n.show() // show the noty dialog
184 | }
185 | }
186 |
187 | /**
188 | * @function settingsAudioFormatSave
189 | * @summary Fetches the value from the audio-format select in the settings UI and triggers the update of the related user-settings-file
190 | * @description Fetches the value from the audio-format select in the settings UI and triggers the update of the related user-settings-file
191 | */
192 | function settingsAudioFormatSave () {
193 | var userSelectedAudioFormat = $('#inputGroupSelectAudio').val() // get value from UI select inputGroupSelectAudio
194 | utils.writeConsoleMsg('info', 'settingsAudioFormatSave ::: User selected the audio format: _' + userSelectedAudioFormat + '_.')
195 | utils.userSettingWrite('audioFormat', userSelectedAudioFormat) // store this value in a json file
196 | }
197 |
198 | /**
199 | * @function settingsOpenExternal
200 | * @summary Gets an url from the settings ui and forwards this to the openURL function
201 | * @description Gets an url from the settings ui and forwards this to the openURL function
202 | * @param {string} url - the actual url
203 | */
204 | function settingsOpenExternal (url) {
205 | utils.openURL(url)
206 | }
207 |
208 | function settingsEnableOrDisableYoutubeDLUpdateButton () {
209 | // check if youtube details file is writeable
210 | // if not - disable thje update button
211 | var youtubeDlBinaryDetailsPath = youtubeDl.binaryDetailsPathGet()
212 | utils.canWriteFileOrFolder(youtubeDlBinaryDetailsPath, function (error, isWritable) {
213 | if (error) {
214 | utils.writeConsoleMsg('error', 'settingsEnableOrDisableYoutubeDLUpdateButton ::: Error while trying to read the youtube-dl details file. Error: ' + error)
215 | throw error
216 | }
217 |
218 | if (isWritable === true) {
219 | // technically we could execute an update if there is one - so lets search for updates
220 | utils.writeConsoleMsg('info', 'settingsEnableOrDisableYoutubeDLUpdateButton ::: Updating youtube-dl binary is technically possible. Button can stay enabled')
221 | $('#buttonSettingsYoutubeDLStartUpdate').prop('title', 'Updating youtube-dl is supported based on your installation permissions.') // set a title to the button
222 | } else {
223 | // details file cant be resetted due to permission issues
224 | utils.writeConsoleMsg('warn', 'settingsEnableOrDisableYoutubeDLUpdateButton ::: Updating youtube-dl binary is not possible on this setup due to permission issues.')
225 | $('#buttonSettingsYoutubeDLStartUpdate').prop('disabled', true) // disable the button
226 | $('#buttonSettingsYoutubeDLStartUpdate').prop('title', 'Updating youtube-dl is disabled based on your installation permissions. The needed file is not writeable') // set a title to the button
227 | }
228 | })
229 | }
230 |
231 | // ----------------------------------------------------------------------------
232 | // EXPORT THE MODULE FUNCTIONS
233 | // ----------------------------------------------------------------------------
234 | //
235 | module.exports.settingsFolderOpen = settingsFolderOpen
236 | module.exports.settingsOpenDevTools = settingsOpenDevTools
237 | module.exports.settingsSelectDownloadDir = settingsSelectDownloadDir
238 | module.exports.settingsToggleVerboseMode = settingsToggleVerboseMode
239 | module.exports.settingsToggleAdditionalParameter = settingsToggleAdditionalParameter
240 | module.exports.settingsSaveAdditionalParameter = settingsSaveAdditionalParameter
241 | module.exports.settingsTogglePrereleases = settingsTogglePrereleases
242 | module.exports.settingsToggleErrorReporting = settingsToggleErrorReporting
243 | module.exports.settingsAudioFormatSave = settingsAudioFormatSave
244 | module.exports.settingsOpenExternal = settingsOpenExternal
245 | module.exports.settingsEnableOrDisableYoutubeDLUpdateButton = settingsEnableOrDisableYoutubeDLUpdateButton
246 |
--------------------------------------------------------------------------------
/app/settings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | media-dupes - settings
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Settings
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
Download directory
86 |
87 |
95 |
96 |
97 |
98 |
Verbose mode
99 |
100 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
Update policy
119 |
120 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
Audio format
137 |
138 |
139 |
140 |
141 | .aac
142 | .flac
143 | .mp3
144 | .m4a
145 | .opus
146 | .ogg
147 | .wav
148 | [best]
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
172 |
173 |
174 |
175 |
Additional youtube-dl parameter
176 |
177 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
Installation
198 |
199 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
Error Reporting & Event Counting
216 |
217 |
225 |
226 |
Additional informations about media-dupes error reporting and event counting can be found here .
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
254 |
255 |
256 |
--------------------------------------------------------------------------------
/app/js/modules/youtubeDl.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Contains all youtubeDl functions
3 | * @author yafp
4 | * @module youtubeDl
5 | */
6 | 'use strict'
7 |
8 | // ----------------------------------------------------------------------------
9 | // REQUIRE MODULES
10 | // ----------------------------------------------------------------------------
11 | //
12 | const utils = require('./utils.js')
13 | const ui = require('./ui.js')
14 |
15 | // ----------------------------------------------------------------------------
16 | // VARIABLES
17 | // ----------------------------------------------------------------------------
18 | //
19 | var youtubeDlBinaryDetailsVersion
20 | var youtubeDlBinaryDetailsPath
21 | var youtubeDLBinaryDetailsExec
22 |
23 | // ----------------------------------------------------------------------------
24 | // YOUTUBE-DL: Arguments which should be filtered out if the user sets them.
25 | // Idea from: https://github.com/mayeaux/videodownloader/blob/master/util.js
26 | // ----------------------------------------------------------------------------
27 | /**
28 | * @constant
29 | * @type {array}
30 | * @default
31 | */
32 | const blacklistedParameter = [
33 | '-h',
34 | '--help',
35 | '-v',
36 | '--version',
37 | '-U',
38 | '--update',
39 | '-q',
40 | '--quiet',
41 | '-s',
42 | '--simulate',
43 | '-g',
44 | '--get-url',
45 | '-e',
46 | '--get-title',
47 | '--get-id',
48 | '--get-thumbnail',
49 | '--get-description',
50 | '--get-duration',
51 | '--get-filename',
52 | '--get-format',
53 | '-j',
54 | '--dump-json',
55 | '--newline',
56 | '--no-progress',
57 | '--console-title',
58 | '--dump-intermediate-pages',
59 | '--write-pages'
60 | ]
61 |
62 | // ----------------------------------------------------------------------------
63 | // YOUTUBE-DL: Binary Details
64 | // ----------------------------------------------------------------------------
65 |
66 | /**
67 | * @function binaryDetailsPathGet
68 | * @summary Gets the path to the youtube-dl binary details file
69 | * @description Gets the path to the youtube-dl binary details file
70 | * @return {string} youtubeDlBinaryDetailsPath - The actual path to the youtube-dl details file
71 | */
72 | function binaryDetailsPathGet () {
73 | const path = require('path')
74 | const remote = require('electron').remote
75 | const app = remote.app
76 |
77 | var youtubeDlBinaryDetailsPath = path.join(app.getAppPath(), 'node_modules', 'youtube-dl', 'bin', 'details')
78 | return (youtubeDlBinaryDetailsPath)
79 | }
80 |
81 | /**
82 | * @function binaryDetailsValueGet
83 | * @summary Gets all values from the youtube-dl binary details file
84 | * @description Gets all values from the youtube-dl binary details file
85 | */
86 | function binaryDetailsValueGet (_callback) {
87 | const fs = require('fs')
88 | youtubeDlBinaryDetailsPath = binaryDetailsPathGet() // get the path to the details file
89 |
90 | fs.readFile(youtubeDlBinaryDetailsPath, 'utf8', function (error, contents) {
91 | if (error) {
92 | utils.writeConsoleMsg('error', 'binaryDetailsValueGet ::: Unable to read youtube-dl binary details values. Error: ' + error + '.')
93 | throw error
94 | } else {
95 | const data = JSON.parse(contents)
96 |
97 | youtubeDlBinaryDetailsVersion = data.version
98 | youtubeDlBinaryDetailsPath = data.path
99 | youtubeDLBinaryDetailsExec = data.exec
100 |
101 | utils.writeConsoleMsg('info', 'binaryDetailsValueGet ::: Version: ' + youtubeDlBinaryDetailsVersion + '.')
102 | utils.writeConsoleMsg('info', 'binaryDetailsValueGet ::: Path: ' + youtubeDlBinaryDetailsPath + '.')
103 | utils.writeConsoleMsg('info', 'binaryDetailsValueGet ::: Exec: ' + youtubeDLBinaryDetailsExec + '.')
104 |
105 | // console.info(data[value])
106 | // var currentDetailsValue = data[value]
107 |
108 | // utils.writeConsoleMsg('warn', 'binaryDetailsValueGet ::: youtube-dl binary details value is version: _' + currentDetailsValue + '_.')
109 | _callback()
110 | }
111 | })
112 | }
113 |
114 | // ----------------------------------------------------------------------------
115 | // YOUTUBE-DL: Binary Path
116 | // ----------------------------------------------------------------------------
117 |
118 | /**
119 | * @function binaryPathGet
120 | * @summary Gets the path to the youtube-dl binary file
121 | * @description Gets the path to the youtube-dl binary file using getYtdlBinary()
122 | * @return {string} youtubeDlBinaryPath - The actual path to the youtube-dl binary
123 | */
124 | function binaryPathGet () {
125 | const youtubedl = require('youtube-dl')
126 | var youtubeDlBinaryPath
127 | youtubeDlBinaryPath = youtubedl.getYtdlBinary() // get the path of the binary
128 | // utils.writeConsoleMsg('info', 'binaryPathGet ::: youtube-dl binary path is: _' + youtubeDlBinaryPath + '_.')
129 | return (youtubeDlBinaryPath)
130 | }
131 |
132 | /**
133 | * @function binaryPathReset
134 | * @summary Resets the youtube-dl binary path in details
135 | * @description Resets the youtube-dl binary path in details
136 | * @param {string} path - The path to the youtube-dl details file
137 | */
138 | function binaryPathReset (path) {
139 | const fs = require('fs')
140 |
141 | fs.readFile(path, 'utf8', function (error, contents) {
142 | if (error) {
143 | utils.writeConsoleMsg('error', 'binaryPathReset ::: Error while trying to read the youtube-dl path. Error: ' + error)
144 | utils.showNoty('error', 'Unable to read the youtube-dl binary details file. Error: ' + error)
145 | throw error
146 | } else {
147 | const data = JSON.parse(contents)
148 | var youtubeDlBinaryPath = data.path
149 | utils.writeConsoleMsg('info', 'binaryPathReset ::: youtube-dl binary path was: _' + youtubeDlBinaryPath + '_ before reset.')
150 |
151 | // now update it
152 | if (youtubeDlBinaryPath !== null) {
153 | // update it back to default
154 | data.path = null
155 | fs.writeFileSync(path, JSON.stringify(data))
156 | utils.writeConsoleMsg('info', 'binaryPathReset ::: Did reset the youtube-dl binary path back to default.')
157 | utils.showNoty('success', 'Did reset the youtube-dl binary path back to default')
158 | } else {
159 | // nothing to do
160 | utils.writeConsoleMsg('info', 'binaryPathReset ::: youtube-dl binary path is: _' + youtubeDlBinaryPath + '_. This is the default')
161 | utils.showNoty('info', 'The youtube-dl binary path was already on its default value. Did no changes.')
162 | }
163 | }
164 | })
165 | }
166 |
167 | // ----------------------------------------------------------------------------
168 | // YOUTUBE-DL: Binary Update
169 | // ----------------------------------------------------------------------------
170 |
171 | /**
172 | * @function binaryUpdateCheck
173 | * @summary Checks if the yoututbe-dl setup allows updating or not
174 | * @description Checks if the yoututbe-dl setup allows updating or not
175 | * @param {boolean} [silent] - Boolean with default value. Shows a feedback in case of no available updates If 'silent' = false. Special handling for manually triggered update search
176 | * @param {boolean} [force] - If progressing is forced or not
177 | */
178 | function binaryUpdateCheck (silent = true, force = false) {
179 | ui.windowMainLoadingAnimationShow()
180 | ui.windowMainApplicationStateSet('Searching updates for youtube-dl binary')
181 |
182 | // check if we could update in general = is details file writeable?
183 | // if not - we can cancel right away
184 | var youtubeDlBinaryDetailsPath = binaryDetailsPathGet()
185 | utils.canWriteFileOrFolder(youtubeDlBinaryDetailsPath, function (error, isWritable) {
186 | if (error) {
187 | utils.writeConsoleMsg('error', 'binaryUpdateCheck ::: Error while trying to read the youtube-dl details file. Error: ' + error)
188 | throw error
189 | }
190 |
191 | if (isWritable === true) {
192 | // technically we could execute an update if there is one - so lets search for updates
193 | utils.writeConsoleMsg('info', 'binaryUpdateCheck ::: Updating youtube-dl binary is technically possible - so start searching for available youtube-dl updates. Silent: _' + silent + '_ and Force: _' + force + '_.')
194 | // var isYoutubeBinaryUpdateAvailable = binaryUpdateSearch(silent, force)
195 | binaryUpdateSearch(silent, force)
196 | } else {
197 | // details file cant be resetted due to permission issues
198 | utils.writeConsoleMsg('warn', 'binaryUpdateCheck ::: Updating youtube-dl binary is not possible on this setup due to permission issues.')
199 |
200 | if (silent === false) {
201 | utils.showNoty('error', 'Unable to update youtube-dl as ' + youtubeDlBinaryDetailsPath + ' is not writeable. This depends most likely on the package/installation type you selected')
202 | ui.windowMainLoadingAnimationHide()
203 | ui.windowMainApplicationStateSet()
204 | }
205 | }
206 | })
207 | }
208 |
209 | /**
210 | * @function binaryUpdateSearch
211 | * @summary Searches for youtube-dl binary updates
212 | * @description Searches for youtube-dl binary updates
213 | * @param {boolean} silent - Defaults to true. If true, the progress is silent, if false there is info-feedback even if there is no update available
214 | * @param {boolean} force - Defaults to false. If enabled the update is forced even if there isa no update available (replacing the existing)
215 | */
216 | function binaryUpdateSearch (silent = true, force = false) {
217 | var remoteAppVersionLatest = '0.0.0'
218 | var localAppVersion = '0.0.0'
219 | var versions
220 |
221 | const urlYTDLGitHubRepoTags = 'https://api.github.com/repos/ytdl-org/youtube-dl/tags' // set youtube-dl API url
222 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Start checking _' + urlYTDLGitHubRepoTags + '_ for available youtube-dl releases. Parameters are silent: _' + silent + '_ and force: _' + force + '_.')
223 |
224 | // var updateStatus = $.get(urlYTDLGitHubRepoTags, function (data, status) {
225 | $.get(urlYTDLGitHubRepoTags, function (data, status) {
226 | // 3000 // in milliseconds
227 |
228 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Accessing _' + urlYTDLGitHubRepoTags + '_ ended with: _' + status + '_')
229 |
230 | // success
231 | versions = data.sort(function (v1, v2) {
232 | // return semver.compare(v2.name, v1.name);
233 | })
234 |
235 | // get remote version
236 | //
237 | remoteAppVersionLatest = versions[0].name
238 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Latest Remote version is: ' + remoteAppVersionLatest)
239 | // remoteAppVersionLatest = '66.6.6'; // overwrite variable to simulate available updates
240 |
241 | // get local version
242 | binaryDetailsValueGet(function () {
243 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Fetched all values from details file')
244 | localAppVersion = youtubeDlBinaryDetailsVersion
245 |
246 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Local youtube-dl binary version: ' + localAppVersion)
247 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Latest youtube-dl binary version: ' + remoteAppVersionLatest)
248 |
249 | if (force === true) {
250 | binaryUpdateExecute() // we are updating - ignoring what is currently installed
251 | } else {
252 | if (localAppVersion < remoteAppVersionLatest) {
253 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Found update for youtube-dl binary. Ask the user if he wants to execute the update now')
254 |
255 | // ask user if he wants to open all those urls
256 | const Noty = require('noty')
257 | var n = new Noty(
258 | {
259 | theme: 'bootstrap-v4',
260 | layout: 'bottom',
261 | type: 'info',
262 | closeWith: [''], // to prevent closing the confirm-dialog by clicking something other then a confirm-dialog-button
263 | text: 'Do you want to update youtube-dl now from ' + localAppVersion + ' to ' + remoteAppVersionLatest + ' ?',
264 | buttons: [
265 | Noty.button('Yes', 'btn btn-success mediaDupes_btnDefaultWidth', function () {
266 | n.close()
267 | binaryUpdateExecute()
268 | },
269 | {
270 | id: 'button1', 'data-status': 'ok'
271 | }),
272 | Noty.button('No', 'btn btn-secondary mediaDupes_btnDefaultWidth float-right', function () {
273 | n.close()
274 | })
275 | ]
276 | })
277 |
278 | n.show() // show the noty dialog
279 | } else {
280 | if (silent === false) {
281 | utils.showNoty('info', 'No youtube-dl binary update available You are already using the latest binary version of youtube-dl')
282 | }
283 | }
284 | }
285 | })
286 |
287 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Successfully checked ' + urlYTDLGitHubRepoTags + ' for available releases')
288 | })
289 | .done(function () {
290 | // utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Successfully checked ' + urlGitHubRepoTags + ' for available releases');
291 | })
292 |
293 | .fail(function () {
294 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Checking ' + urlYTDLGitHubRepoTags + ' for available releases failed.')
295 | utils.showNoty('error', 'Checking ' + urlYTDLGitHubRepoTags + ' for available releases failed. Please troubleshoot your network connection.', 0)
296 | })
297 |
298 | .always(function () {
299 | utils.writeConsoleMsg('info', 'binaryUpdateSearch ::: Finished checking ' + urlYTDLGitHubRepoTags + ' for available releases')
300 | ui.windowMainLoadingAnimationHide()
301 | ui.windowMainButtonsOthersEnable()
302 | ui.windowMainApplicationStateSet()
303 | })
304 | }
305 |
306 | /**
307 | * @function binaryUpdateExecute
308 | * @summary Updates the youtube-dl binary
309 | * @description Updates the youtube-dl binary
310 | */
311 | function binaryUpdateExecute () {
312 | // const youtubedl = require('youtube-dl')
313 | const downloader = require('youtube-dl/lib/downloader')
314 |
315 | const remote = require('electron').remote
316 | const app = remote.app
317 | const path = require('path')
318 | const targetPath = path.join(app.getPath('userData'), 'youtube-dl') // set targetPath
319 |
320 | // start downloading latest youtube-dl binary to custom path
321 | downloader(targetPath, function error (error, done) {
322 | 'use strict'
323 | if (error) {
324 | utils.writeConsoleMsg('error', 'binaryUpdateExecute ::: Error while trying to update the youtube-dl binary at: _' + targetPath + '_. Error: ' + error)
325 | utils.showNoty('error', 'Unable to update youtube-dl binary. ' + error, 0)
326 | throw error
327 | }
328 | utils.writeConsoleMsg('info', 'binaryUpdateExecute ::: Updated youtube-dl binary at: _' + targetPath + '_.')
329 | console.log(done)
330 | utils.showNoty('success', done)
331 | })
332 | }
333 |
334 | // ----------------------------------------------------------------------------
335 | // EXPORT THE MODULE FUNCTIONS
336 | // ----------------------------------------------------------------------------
337 | //
338 | module.exports.blacklistedParameter = blacklistedParameter
339 | module.exports.binaryDetailsPathGet = binaryDetailsPathGet
340 | module.exports.binaryDetailsValueGet = binaryDetailsValueGet
341 | module.exports.binaryPathGet = binaryPathGet
342 | module.exports.binaryPathReset = binaryPathReset
343 | module.exports.binaryUpdateCheck = binaryUpdateCheck
344 | module.exports.binaryUpdateSearch = binaryUpdateSearch
345 | module.exports.binaryUpdateExecute = binaryUpdateExecute
346 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | media-dupes
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | Loading...
103 |
104 |
105 |
106 |
media-dupes
107 |
108 |
109 |
112 |
113 |
114 |
a minimal content duplicator for common media services like youtube
115 |
116 |
117 |
118 |
119 |
120 |
121 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | ID
134 |
135 |
136 |
137 |
138 | Url (long)
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
162 | Video
163 |
164 |
165 |
166 |
179 |
180 |
181 |
182 |
192 | Audio
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
225 | Settings
226 |
227 |
228 |
235 | Intro
236 |
237 |
238 |
245 | Downloads
246 |
247 |
248 |
256 |
257 |
258 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
393 |
394 |
395 |
--------------------------------------------------------------------------------
/docs/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # media-dupes
4 | ## changelog
5 |
6 | This project is using [Semantic Versioning](https://semver.org/).
7 |
8 | `
9 | MAJOR.MINOR.PATCH
10 | `
11 |
12 | * `MAJOR` version (incompatible API changes etc)
13 | * `MINOR` version (adding functionality)
14 | * `PATCH` version (bug fixes)
15 |
16 |
17 | The following categories are used:
18 |
19 | * `Added`: for new features
20 | * `Changed`: for changes in existing functionality.
21 | * `Deprecated`: for soon-to-be removed features.
22 | * `Removed`: for now removed features.
23 | * `Fixed`: for any bug fixes.
24 | * `Security`: in case of vulnerabilities.
25 |
26 |
27 | ***
28 |
29 | ### media-dupes 0.10.3 (20200801)
30 | #### `Changed`
31 | * Updated dependencies:
32 | * Updated `eslint` from `7.5.0` to `7.6.0`.
33 |
34 | #### `Fixed`
35 | * Fixed a dependency error. See [#140](https://github.com/yafp/media-dupes/issues/140)
36 |
37 |
38 | ***
39 |
40 | ### media-dupes 0.10.2 (20200730)
41 | #### `Changed`
42 | * Updated dependencies:
43 | * Updated `custom-electron-titlebar` from `3.2.2` to `3.2.3`.
44 | * Updated `fontawesome-free` from `5.13.0` to `5.14.0`.
45 | * Updated `electron` from `9.0.3` to `9.1.2`.
46 | * Updated `electron-builder` from `22.7.0` to `22.8.0`
47 | * Updated `electron-log` from `4.2.1` to `4.2.2`.
48 | * Updated `electron-packager` from `14.2.1` to `15.0.0`.
49 | * Updated `electron-util` from `0.14.1` to `0.14.2`.
50 | * Updated `electron-json-storage` from `4.1.8` to `4.2.0`.
51 | * Updated `electron-prompt` from `1.5.0` to `1.6.0`.
52 | * Updated `eslint` from `7.2.0` to `7.5.0`.
53 | * Updated `got` from `11.3.0` to `11.5.1`
54 | * Updated `jsdoc` from `3.6.4` to `3.6.5`.
55 | * Updated `metascraper-media-provider` from `5.12.0` to `5.13.0`
56 | * Updated `metascraper` from `5.11.21` to `5.13.1`.
57 | * Updated `metascraper-audio` from `5.11.21` to `5.13.1`.
58 | * Updated `metascraper-description` from `5.11.21` to `5.13.1`.
59 | * Updated `metascraper-image` from `5.11.21` to `5.13.1`.
60 | * Updated `metascraper-logo` from `5.11.21` to `5.13.1`.
61 | * Updated `metascraper-logo-favicon` from `5.11.21` to `5.13.1`.
62 | * Updated `metascraper-media-provider` from `5.11.22` to `5.13.1`.
63 | * Updated `metascraper-soundcloud` from `5.11.21` to `5.13.1`.
64 | * Updated `metascraper-title` from `5.11.21` to `5.13.1`.
65 | * Updated `metascraper-video` from `5.11.21` to `5.13.1`.
66 | * Updated `metascraper-youtube` from `5.11.21` to `5.13.1`.
67 | * Updated `ntl` from `5.0.0` to `5.1.0`.
68 | * Updated `sentry/electron` from `1.3.0` to `1.5.2`.
69 | * Updated `yargs` from `15.3.1` to `15.4.1`.
70 |
71 | #### `Fixed`
72 | * Fixed a TypeError. See [#133](https://github.com/yafp/media-dupes/issues/133)
73 |
74 |
75 | ***
76 |
77 | ### media-dupes 0.10.1 (20200609)
78 | #### `Changed`
79 | * Updated dependencies:
80 | * Updated `electron` from `9.0.2` to `9.0.3`.
81 |
82 | #### `Fixed`
83 | * Fixed a dependency error. See [#130](https://github.com/yafp/media-dupes/issues/130)
84 |
85 |
86 | ***
87 |
88 | ### media-dupes 0.10.0 (20200607)
89 | #### `Added`
90 | * Added todo-list-protection to ensure items of the list arent removed while media-dupes is processing the list. See [#127](https://github.com/yafp/media-dupes/issues/127)
91 | * Added a warning dialog about consequences of not updating media-dupes. See [#129](https://github.com/yafp/media-dupes/issues/129)
92 |
93 | #### `Changed`
94 | * Improved update check - using semver for version comparison.
95 | * Updated dependencies:
96 | * Updated `about-window` from `1.13.2` to `1.13.4`
97 | * Updated `datatables.net-dt` from `1.10.20` to `1.10.21`
98 | * Updated `datatables.net-scroller-dt` from `2.0.1` to `2.0.2`
99 | * Updated `electron` from `8.2.5` to `9.0.2`. See [#128](https://github.com/yafp/media-dupes/issues/128)
100 | * Updated `electron-builder` from `22.6.0` to `22.7.0`
101 | * Updated `electron-log` from `4.1.2` to `4.2.1`
102 | * Updated `eslint` from `6.8.0` to `7.2.0`
103 | * Updated `got` from `10.7.0` to `11.3.0`
104 | * Updated `jquery` from `3.5.0` to `3.5.1`
105 | * Updated `metascraper` from `5.11.10` to `5.11.21`
106 | * Updated `metascraper-audio` from `5.11.10` to `5.11.21`
107 | * Updated `metascraper-description` from `5.11.10` to `5.11.21`
108 | * Updated `metascraper-image` from `5.11.10` to `5.11.21`
109 | * Updated `metascraper-logo` from `5.11.10` to `5.11.21`
110 | * Updated `metascraper-logo-favicon` from `5.11.11` to `5.11.21`
111 | * Updated `metascraper-media-provider` from `5.11.11` to `5.11.22`
112 | * Updated `metascraper-soundcloud` from `5.11.11` to `5.11.21`
113 | * Updated `metascraper-title` from `5.11.10` to `5.11.21`
114 | * Updated `metascraper-video` from `5.11.10` to `5.11.21`
115 | * Updated `metascraper-youtube` from `5.11.10` to `5.11.21`
116 | * Updated `v8-compile-cache` from `2.1.0` to `2.1.1`
117 |
118 | ***
119 |
120 | ### media-dupes 0.9.0 (20200502)
121 | #### `Added`
122 | * Added prelisten function to todo-list. See [#119](https://github.com/yafp/media-dupes/issues/119)
123 | * Added 'show supported sites' menu entry to the help -> youtube-dl menu. See [#113](https://github.com/yafp/media-dupes/issues/113)
124 | * Added support for cli parameters. See [#115](https://github.com/yafp/media-dupes/issues/115)
125 | * Added icons to menu. See [#118](https://github.com/yafp/media-dupes/issues/118)
126 |
127 | #### `Changed`
128 | * UI:
129 | * Reduced minimal window height from 830px to 730 px. See [#111](https://github.com/yafp/media-dupes/issues/111)
130 | * Settings icon on settings window now shows its function using a title.
131 | * ToDoList: Content of column url is now truncated to ensure the table does not break. See [#120](https://github.com/yafp/media-dupes/issues/120)
132 | * Disclaimer: Added blur and unblur. Changed timing for disclaimer-check call (moved from ready-to-show to show).
133 | * Sentry: Removed several event count events to reduce overall amount of generated reports.
134 | * New default value for `app.allowRendererProcessReuse` is now `true`.
135 | * Updated dependencies:
136 | * Updated `electron` from `8.2.1` to `8.2.5`
137 | * Updated `electron-builder` from `22.4.1` to `22.6.0`
138 | * Updated `electron-log` from `4.1.1` to `4.1.2`
139 | * Updated `electron-util` from `0.14.0` to `0.14.1`
140 | * Updated `metascraper` from `5.11.8` to `5.11.10`
141 | * Updated `metascraper-audio` from `5.11.8` to `5.11.10`
142 | * Updated `metascraper-description` from `5.11.8` to `5.11.10`
143 | * Updated `metascraper-image` from `5.11.8` to `5.11.10`
144 | * Updated `metascraper-logo` from `5.11.8` to `5.11.10`
145 | * Updated `metascraper-logo-favicon` from `5.11.8` to `5.11.11`
146 | * Updated `metascraper-media-provider` from `5.11.8` to `5.11.11`
147 | * Updated `metascraper-soundcloud` from `5.11.8` to `5.11.10`
148 | * Updated `metascraper-title` from `5.11.8` to `5.11.10`
149 | * Updated `metascraper-video` from `5.11.8` to `5.11.10`
150 | * Updated `metascraper-youtube` from `5.11.8` to `5.11.10`
151 |
152 | #### `Removed`
153 | * Removed the show extractors function from the main UI. Users can still see a list of supported youtube-dl sites via the help menu. See [#121](https://github.com/yafp/media-dupes/issues/121)
154 |
155 | #### `Fixed`
156 | * Fixed a minor issue in todo-list if metascraper got null values. See [#114](https://github.com/yafp/media-dupes/issues/114)
157 |
158 | ***
159 |
160 | ### media-dupes 0.8.0 (20200411)
161 | #### `Added`
162 | * ToDo list
163 | * Single urls can now be removed from list. See [#104](https://github.com/yafp/media-dupes/issues/104)
164 | * Now shows the url favicon. See [#107](https://github.com/yafp/media-dupes/issues/107)
165 | * Now shows the preview image. See [#108](https://github.com/yafp/media-dupes/issues/108)
166 | * New Dependencies
167 | * Added `datatables.net-dt` version `1.10.20`. See [#102](https://github.com/yafp/media-dupes/issues/102)
168 | * Added `datatables.net-scroller-dt` version `2.0.1`. See [#102](https://github.com/yafp/media-dupes/issues/102)
169 | * Added `md5` version `2.2.1`.
170 | * Added `got` version `10.7.0`.
171 | * Added `metascraper` version `5.11.8`.
172 | * Added `metascraper-audio` version `5.11.8`.
173 | * Added `metascraper-description` version `5.11.8`.
174 | * Added `metascraper-image` version `5.11.8`.
175 | * Added `metascraper-logo` version `5.11.8`.
176 | * Added `metascraper-logo-favicon` version `5.11.8`.
177 | * Added `metascraper-media-provider` version `5.11.8`.
178 | * Added `metascraper-soundcloud` version `5.11.8`.
179 | * Added `metascraper-title` version `5.11.8`.
180 | * Added `metascraper-video` version `5.11.8`.
181 | * Added `metascraper-youtube` version `5.11.8`.
182 |
183 | #### `Changed`
184 | * ToDo list is now a table. See [#102](https://github.com/yafp/media-dupes/issues/102)
185 | * Reset UI is now reloading the UI.
186 | * Changed notifcation-display-time on end of download queue. Info hides itself after a while. Warning and error stay until confirmed.
187 | * Dependencies
188 | * Updated `electron` from `8.2.0` to `8.2.1`
189 | * Updated `jsdoc` from `3.6.3` to `3.6.4`
190 | * Updated `jquery` from `3.4.1` to `3.5.0`
191 |
192 | #### `Removed`
193 | * Removed youtube-dl setting 'fetch url informations'. See [#109](https://github.com/yafp/media-dupes/issues/109)
194 | * Removed is-reachable test for urls as it was not reliable. See [#106](https://github.com/yafp/media-dupes/issues/106)
195 | * CI
196 | * Removing travis and appveyor - as building is now realized using GitHub actions.
197 |
198 | #### `Fixed`
199 | * Fixed an error in todo-list saving and restoring. See [#105](https://github.com/yafp/media-dupes/issues/105)
200 | * Fixed an error in todo-list cleanup. See [#110](https://github.com/yafp/media-dupes/issues/110)
201 |
202 |
203 | ***
204 |
205 | ### media-dupes 0.7.0 (20200403)
206 | #### `Added`
207 | * Energy-management
208 | * Added a power-save-blocker. Now trying to prevent powerSave while downloads are in progress. See [#97](https://github.com/yafp/media-dupes/issues/97)
209 |
210 | #### `Changed`
211 | * Audio-Mode: Added tracknumer as first parameter for the naming pattern of audio files.
212 | * Windows nsis installer. Show install and uninstall details. See [#96](https://github.com/yafp/media-dupes/issues/96)
213 | * Removed most of the event count functions. Only core functionality is counted from now. See [#103](https://github.com/yafp/media-dupes/issues/103)
214 | * Settings
215 | * youtube-dl: Update button is now disabled if update is technically not possible.
216 | * Dependencies
217 | * Updated `electron` from `8.1.0` to `8.2.0`
218 | * Updated `electron-builder` from `22.4.0` to `22.4.1`
219 | * Updated `electron-log` from `4.0.7` to `4.1.1`
220 | * Updated `fontaswesome-free` from `5.12.1` to `5.13.0`
221 | * Updated `sentry` from `1.2.1` to `1.3.0`
222 | * Updated `mocha` from `7.1.0` to `7.1.1`
223 |
224 | #### `Fixed`
225 | * youtube-dl update routine
226 | * Improved handling of 'Force updating youtube-dl binary ' function via menu.
227 | * Added error handling to 'reset youtube-dl binary path' function via menu.
228 | * Fixed update issues due to missing permissions. See [#98](https://github.com/yafp/media-dupes/issues/98)
229 | * Fixed horizontal scrollbar bug. See [#100](https://github.com/yafp/media-dupes/issues/100)
230 | * Fixed some vulnerabilities in dependencies.
231 | * Fixed an issue when the configured download target no longer exists. Now fallbacks to default
232 |
233 | ***
234 |
235 | ### media-dupes 0.6.0 (20200306)
236 | #### `Added`
237 | * URL input: Url-input-field is now color-coded (red = unreachable, yellow = unchecked, green = reachable). See [#82](https://github.com/yafp/media-dupes/issues/82)
238 | * Added splash screen. See [#78](https://github.com/yafp/media-dupes/issues/83)
239 | * Energy-management
240 | * Added todoList save function on sleep event. See [#79](https://github.com/yafp/media-dupes/issues/79)
241 | * Added todoList restore function on resume event. See [#79](https://github.com/yafp/media-dupes/issues/79)
242 | * Audio: Added thumbnail-embedding support for .m4a
243 | * Log: Added support for leading timestamps in the log. See [#84](https://github.com/yafp/media-dupes/issues/84)
244 | * Added youtube suggestion function. See [#86](https://github.com/yafp/media-dupes/issues/86)
245 | * Added getInfo routine for urls, executed when Url is added to queue. See [#87](https://github.com/yafp/media-dupes/issues/87)
246 | * Added url thumbnail preview (after adding an url to queue). See [#89](https://github.com/yafp/media-dupes/issues/89)
247 | * Added support for additional youtube-dl flags/parameter by using the settings UI. See [#88](https://github.com/yafp/media-dupes/issues/88)
248 | * Added new setting: Fetch url informations. See [#95](https://github.com/yafp/media-dupes/issues/95)
249 | * Added `v8-compile-cache` to the project.
250 |
251 | #### `Changed`
252 | * Changed application icon
253 | * Improved download resume for user. See [#78](https://github.com/yafp/media-dupes/issues/78)
254 | * Changed the configurations options for the Windows Installer (NSIS). See [#72](https://github.com/yafp/media-dupes/issues/72)
255 | * Moved settings code to module. See [#77](https://github.com/yafp/media-dupes/issues/77)
256 | * Changed textarea font to monospace family to ensure correct indenting in the log
257 | * Added youtube-dl flag `--restrict-filenames` to the parameter list.
258 | * URL Restore: Added makeUrgent notification to the end of a successful executed URL restore.
259 | * Settings: Initial settings creation with default values is now silent. See [#80](https://github.com/yafp/media-dupes/issues/80)
260 | * Removed success notification on 'Loading supported extractors'.
261 | * Standardx: Added `snazzy` to `npm run standardx` and `npm run standardx-fix` scripts to enable funky output for developers.
262 | * OS notifications: Clicking the notification is now raising the application UI. See [#85](https://github.com/yafp/media-dupes/issues/85)
263 | * UI
264 | * Fonts: Added Arial and ArialMono to the project to ensure the same font is used on all installations. See [#94](https://github.com/yafp/media-dupes/issues/94)
265 | * mainWindow: Changed the layout. Using the entire window height now - Part 1. See [#42](https://github.com/yafp/media-dupes/issues/42)
266 | * mainWindow: added fadeIn effect to entire body
267 | * mainWindow: Blur effect now affects as well the titlebar. See [#91](https://github.com/yafp/media-dupes/issues/91)
268 | * Settings: added fadeIn effect to entire body
269 | * Settings: now using tabs. See [#93](https://github.com/yafp/media-dupes/issues/93)
270 | * Update checks
271 | * Search fopr media-dupes updates is now triggered once 5 second after app start. Might speed up the start
272 | * Search for youtube-dl updates is now triggered once 5 seconds after app start. Might speed up the start
273 | * Dependencies
274 | * Updated `custom-electron-titlebar` from `3.2.1` to `3.2.2`
275 | * Updated `electron` from `7.1.10` to `8.1.0`
276 | * Updated `electron-log` from `4.0.4` to `4.0.7`
277 | * Updated `electron-packager` from `14.2.0` to `14.2.1`
278 | * Updated `electron-builder` from `22.3.2` to `22.4.0`
279 | * Updated `electron-util` from `0.13.1` to `0.14.0`
280 | * Updated `fontawesome` from `5.12.0` to `5.12.1`
281 | * Updated `mocha` from `7.0.1` to `7.1.0`
282 | * Updated `rimraf` from `3.0.1` to `3.0.2`
283 | * Updated `spectron` from `10.0.0` to `10.0.1`
284 | * Updated `sentry` from `1.2.0` to `1.2.1`
285 | * Updated `youtube-dl` from `3.0.1` to `3.0.2`
286 |
287 | #### `Removed`
288 | * Removed `npx` from project. See [#41](https://github.com/yafp/media-dupes/issues/41)
289 |
290 | #### `Fixed`
291 | * Fixed undefined version in sentry error events. See [#75](https://github.com/yafp/media-dupes/issues/75)
292 | * Fixed issues with the windows installer (NSIS). See [#76](https://github.com/yafp/media-dupes/issues/76)
293 | * Fixed a bug with the Intro. See [#92](https://github.com/yafp/media-dupes/issues/92)
294 |
295 | ***
296 |
297 | ### media-dupes 0.5.0 (20200129)
298 | #### `Added`
299 | * Added a disclaimer which must be confirmed once per user. See [#52](https://github.com/yafp/media-dupes/issues/52)
300 | * Added support for saving and restoring urls. See [#66](https://github.com/yafp/media-dupes/issues/66)
301 | * Added support for applicationState. Ask user if he really wants to quit when downloads are in progress. See [#59](https://github.com/yafp/media-dupes/issues/59)
302 | * Added some youtube-dl maintenance function to the menu. See [#57](https://github.com/yafp/media-dupes/issues/57)
303 | * Reset youtube-dl binary path (to revert back to bundled youtube-dl binary)
304 | * Force updating youtube-dl binary (to redownload the latest stable binary)
305 | * Added general support for UI animations/effects using animate.js. See [#69](https://github.com/yafp/media-dupes/issues/69)
306 | * Adding support for new audio formats. See [#65](https://github.com/yafp/media-dupes/issues/65)
307 | * Added support for `.aac`
308 | * Added support for `.flac`
309 | * Added support for `.opus`
310 | * Added support for `.ogg/vorbis`
311 | * Added support for the option `best`
312 | * Added basic support for powerMonitoring (suspend and resume). See [#67](https://github.com/yafp/media-dupes/issues/67)
313 | * Added new user setting `verbose mode`. See [#70](https://github.com/yafp/media-dupes/issues/70)
314 |
315 | #### `Changed`
316 | * Update search:
317 | * Is now ignoring pre-releases. See [#73](https://github.com/yafp/media-dupes/issues/73)
318 | * Added new setting to configure search for pre-releases. See [#74](https://github.com/yafp/media-dupes/issues/74)
319 | * Setting UI:
320 | * is now a child window of the main UI. See [#58](https://github.com/yafp/media-dupes/issues/58)
321 | * is now a modal window. See [#63](https://github.com/yafp/media-dupes/issues/63)
322 | * while setting UI is open the main UI gets blur'ed. See [#64](https://github.com/yafp/media-dupes/issues/64)
323 | * Improved validation of youtube-dl setup. See [#56](https://github.com/yafp/media-dupes/issues/56)
324 | * Show extractors function not longer resets the log. It appends now the new data.
325 | * Downloading audio:
326 | * Improved filename pattern for audio downloads. See [#61](https://github.com/yafp/media-dupes/issues/61)
327 | * Added `--add-metadata` flag.
328 | * Added `--ignore-errors` flag.
329 | * Download video:
330 | * Added `--ignore-errors` flag.
331 | * Improved audio quality setting by using `--audio-quality 0` (was set to 5 before).
332 | * Moved functions from renderer to new modules
333 | * ffmpeg
334 | * youtubeDl
335 | * ui
336 | * sentry
337 | * Dependencies
338 | * Updated `youtube-dl` from 3.0.0 to 3.0.1
339 | * Updated `popper.js` from 1.16.0 to 1.16.1
340 | * Updated `electron` from 7.1.9 to 7.1.10
341 | * Updated `electron-log` from 4.0.3 to 4.0.4
342 | * Updated `spectron` from 9.0.0 to 10.0.0
343 | * Updated `mocha` from 7.0.0 to 7.0.1
344 | * Updated `docdash` from 1.1.0 to 1.2.0
345 | * Updated `electron-builder` from 22.2.0 to 22.3.2
346 | * Updated `rimraf` from 3.0.0 to 3.0.1
347 | * Documentation: Improved jsdoc documentation. Adding namespaces and some other changes
348 | * Builds: Improved the macOS .dmg style. New background and icon positions
349 |
350 | #### `Fixed`
351 | * Fixed several errors in application log showing wrong urls and progress-state informations. See [#60](https://github.com/yafp/media-dupes/issues/60)
352 | * Fixed error handling when downloading a single url failed. See [#71](https://github.com/yafp/media-dupes/issues/71)
353 | * Fixed a vertical scrollbar bug
354 |
355 | ***
356 |
357 | ### media-dupes 0.4.2 (20200116)
358 |
359 | #### `Fixed`
360 | * Fixed the broken .icns app icon. See [#54](https://github.com/yafp/media-dupes/issues/54)
361 | * Fixed wrong path information in youtube-dl's detail file. See [#55](https://github.com/yafp/media-dupes/issues/55)
362 |
363 |
364 | ***
365 |
366 | ### media-dupes 0.4.1 (20200115)
367 |
368 | #### `Changed`
369 | * Added `rimraf` to project for package.json scripts to improve clean scripts. See [#48](https://github.com/yafp/media-dupes/issues/48)
370 | * Simplified requirements check on startup. See [#49](https://github.com/yafp/media-dupes/issues/49)
371 | * Dependencies
372 | * Updated `electron-builder` from 21.2.0 to 22.2.0
373 |
374 | #### `Fixed`
375 | * Fixed a bug in youtube-dl binary update routine. See [#50](https://github.com/yafp/media-dupes/issues/50)
376 |
377 | ***
378 |
379 | ### media-dupes 0.4.0 (20200114)
380 | #### `Added`
381 | * Error reporting using sentry is now optional via application settings. See [#31](https://github.com/yafp/media-dupes/issues/31)
382 | * Added background images to textareas (todo-list and log). See [#35](https://github.com/yafp/media-dupes/issues/35)
383 | * Added confirm dialog to UI reset function. See [#37](https://github.com/yafp/media-dupes/issues/37)
384 | * Added update check for youtube-dl binary. See [#40](https://github.com/yafp/media-dupes/issues/40)
385 | * Added update function for youtube-dl binary. See [#34](https://github.com/yafp/media-dupes/issues/34)
386 | * Added update check for youtube-dl binary on app startup. See [#40](https://github.com/yafp/media-dupes/issues/40)
387 |
388 | #### `Changed`
389 | * Improving error handling
390 | * By adding `unhandled` with dialogs & report issue to github function. See [#46](https://github.com/yafp/media-dupes/issues/46)
391 | * Sentry: Enabled `sentry` debug mode. See [#36](https://github.com/yafp/media-dupes/issues/36)
392 | * UI
393 | * General: Disabling most UI buttons while execution of some functions (searching for updates, loading extractors) to prevent race-conditions. See [#33](https://github.com/yafp/media-dupes/issues/33)
394 | * General: Reduced minimal window height about 60px. See [#38](https://github.com/yafp/media-dupes/issues/38)
395 | * Settings: reduced ui-element size on settings page from default to small. See [#38](https://github.com/yafp/media-dupes/issues/38)
396 | * Settings: show `youtube-dl` binary version. See [#39](https://github.com/yafp/media-dupes/issues/39)
397 | * Improved handling if user tries to add un-useable urls (focus to input field & selecting the content if possible).
398 | * Moved some helper functions to `app/js/modules/mdUtils.js`
399 | * Dependencies
400 | * Updated `electron` from 7.1.6 to 7.1.9
401 | * Updated `electron-log` from 4.0.0 to 4.0.3
402 | * Updated `electron-packager` from 14.1.1 to 14.2.0
403 | * Updated `eslint` from 6.7.2 to 6.8.0
404 | * Updated `mocha` from 6.2.2 to 7.0.0
405 | * Updated `sentry` from 1.1.0 to 1.2.0
406 | * Updated `youtube-dl` from 2.3.0 to 3.0.0
407 | * Switching back from `pj-custom-electron-titlebar` to `custom-electron-titlebar` (3.2.1)
408 | * Added missing timeout = 0 to several noty error dialogs (ensure the error must be confirmed)
409 |
410 | #### `Removed`
411 | * Removed any `sentry` usage which was not error-focused (no user tracking). See [#31](https://github.com/yafp/media-dupes/issues/31)
412 |
413 | #### `Fixed`
414 | * Fixed error with non-defined array. See [#30](https://github.com/yafp/media-dupes/issues/30)
415 | * Fixed error URIError: URI malformed. See [#25](https://github.com/yafp/media-dupes/issues/25)
416 | * Fixed error where detecting `youtube-dl` binary was not working on packaged-builds. See [#44](https://github.com/yafp/media-dupes/issues/44)
417 | * Fixed a bug affecting all windows build containing the wrong yotube-dl binary. See [#47](https://github.com/yafp/media-dupes/issues/47)
418 |
419 | ***
420 |
421 | ### media-dupes 0.3.0 (20191219)
422 | #### `Added`
423 | * Added an error dialog to show issues with the spawned download process. See [#25](https://github.com/yafp/media-dupes/issues/25)
424 | * Settings: Added buttons to visit `youtube-dl` and `ffmpeg` project pages. See [#29](https://github.com/yafp/media-dupes/issues/29)
425 |
426 | #### `Changed`
427 | * Reduced build size by only adding ffmpeg for the actual platform. See [#22](https://github.com/yafp/media-dupes/issues/22)
428 | * Improved url detection from clipboard (trim leading and trailing blanks). See [#28](https://github.com/yafp/media-dupes/issues/28)
429 | * Downloading: Added decode function for user urls to avoid the risk of malformed urls. See [#25](https://github.com/yafp/media-dupes/issues/25)
430 | * Added fade-in effect to load process of the .html files (index.html and settings.html).
431 | * Extractors: Show extractors list now shows an error notification if fetching them fails. See [#27](https://github.com/yafp/media-dupes/issues/27)
432 | * Improved adding urls (trim leading and trailing blanks). See [#28](https://github.com/yafp/media-dupes/issues/28)
433 | * Using intro.js now via npm. See [#21](https://github.com/yafp/media-dupes/issues/21)
434 | * UI: Added a left/right/bottom border for the UI (css)
435 | * Dependencies
436 | * Updated `electron` from 7.1.4 to 7.1.6
437 | * Updated `youtube-dl` from 2.2.0 to 2.3.0
438 |
439 | #### `Fixed`
440 | * Fixed issue where the search for software updates was launched twice on application start. See [#26](https://github.com/yafp/media-dupes/issues/26)
441 | * Fixed an issue where the Loading-animation might be hidden, while it should be still displayed.
442 |
443 | ***
444 |
445 | ### media-dupes 0.2.0 (20191213)
446 | #### `Added`
447 | * Added icon to os-notifications. See [#5](https://github.com/yafp/media-dupes/issues/5)
448 | * Added custom-titlebar (merging menu and titlebar to one line). See [#6](https://github.com/yafp/media-dupes/issues/6)
449 | * Added support for urgent window after finishing all download jobs. See [#7](https://github.com/yafp/media-dupes/issues/7)
450 | * Added loading/spinner to show ongoing download process. See [#8](https://github.com/yafp/media-dupes/issues/8)
451 | * Added an intro to the UI using introJs. See [#14](https://github.com/yafp/media-dupes/issues/14)
452 | * Added settings UI. See [#19](https://github.com/yafp/media-dupes/issues/19)
453 | * Added support for user specific target audio formats (mp3, m4a, wav)
454 | * Added support for custom download dir
455 | * Added settings entry to the 'File' menu. See [#19](https://github.com/yafp/media-dupes/issues/19)
456 | * Added support for save and restore window position and size. See [#20](https://github.com/yafp/media-dupes/issues/20)
457 | * Added auto-paste of urls on focus. See [#24](https://github.com/yafp/media-dupes/issues/24)
458 |
459 |
460 | #### `Changed`
461 | * Download directory / handling
462 | * Audio: downloads are now located in a sub-directory 'Audio' inside the target download dir
463 | * Audio: media-dupes tries to create download specific directories inside Audio to improve handling of albums. If that fails the download lands in a subfolder called NA-NA
464 | * Dependencies check on application launch is now searching for youtube-dl as well.
465 | * Dependencies
466 | * Updated `electron` from 7.1.3 to 7.1.4
467 | * Updated `youtube-dl` from 2.1.0 to 2.2.0
468 | * Updated `fontawesome` from 5.11.2 to 5.12.0
469 | * Updated `sentry` from 1.1.0-beta to 1.1.0
470 | * Normalized code style using standardx
471 | * Improved youtube-dl flag usage
472 | * added: `--add-metadata` for video. See [#10](https://github.com/yafp/media-dupes/issues/10)
473 | * added: `--embed-thumbnail` for audio/mp3. See [#11](https://github.com/yafp/media-dupes/issues/11)
474 | * added: `--ignore-errors` for audio & video. See [#12](https://github.com/yafp/media-dupes/issues/12)
475 | * Log section now auto-scrolls to bottom of log while downloading content. See [#13](https://github.com/yafp/media-dupes/issues/13)
476 | * Log section is now showing more informations as we are now using the youtube-dl flag `--verbose`
477 |
478 | #### `Removed`
479 | * Menu
480 | * Removed the option 'View' - 'Hide'. As there was no option to access the hidden window anymore.
481 | * Builds
482 | * No more .zip Builds for macOS
483 |
484 | ***
485 |
486 | ### media-dupes 0.1.0 (20191209)
487 | #### `Added`
488 | * Initial version of media-dupes
489 | * Core functions
490 | * Download video. See [#1](https://github.com/yafp/media-dupes/issues/1)
491 | * Downloading/extracting audio. See [#2](https://github.com/yafp/media-dupes/issues/2)
492 | * Minor functions
493 | * Show extractor list.See [#4](https://github.com/yafp/media-dupes/issues/4)
494 | * Open local download folder
495 | * Application menu
496 | * about window
497 | * check for software updates. See [#3](https://github.com/yafp/media-dupes/issues/3)
498 | * Basic support for in-app notification using noty
499 | * Basic support for os notifications
500 |
--------------------------------------------------------------------------------
/app/js/modules/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Contains all helper and utility functions
3 | * @author yafp
4 | * @module utils
5 | */
6 | 'use strict'
7 |
8 | // ----------------------------------------------------------------------------
9 | // REQUIRE MODULES
10 | // ----------------------------------------------------------------------------
11 | //
12 | const sentry = require('./sentry.js')
13 | const ui = require('./ui.js')
14 |
15 | /**
16 | * @function writeConsoleMsg
17 | * @summary Writes console output for the renderer process
18 | * @description Writes console output for the renderer process
19 | * @param {string} type - String which defines the log type
20 | * @param {string} message - String which defines the log message
21 | * @param {string} optionalObject - An optional object which might contain additional informations
22 | */
23 | function writeConsoleMsg (type, message, optionalObject = '') {
24 | const logR = require('electron-log')
25 | const prefix = '[ Renderer ] '
26 |
27 | // electron-log can: error, warn, info, verbose, debug, silly
28 | switch (type) {
29 | case 'info':
30 | logR.info(prefix + message, optionalObject)
31 | break
32 |
33 | case 'warn':
34 | logR.warn(prefix + message, optionalObject)
35 | break
36 |
37 | case 'error':
38 | logR.error(prefix + message, optionalObject)
39 | break
40 |
41 | default:
42 | logR.silly(prefix + message, optionalObject)
43 | break
44 | }
45 | }
46 |
47 | /**
48 | * @function showNoty
49 | * @summary Shows a noty notification
50 | * @description Creates an in-app notification using the noty framework
51 | * @param {string} type - Options: alert, success, warning, error, info/information
52 | * @param {string} message - notification text
53 | * @param {number} [timeout] - Timevalue, defines how long the message should be displayed. Use 0 for no-timeout
54 | */
55 | function showNoty (type, message, timeout = 3000) {
56 | const Noty = require('noty')
57 | new Noty({
58 | type: type,
59 | timeout: timeout,
60 | theme: 'bootstrap-v4',
61 | layout: 'bottom',
62 | text: message,
63 | animation: {
64 | open: 'animated fadeIn', // Animate.css class names: default: bounceInRight
65 | close: 'animated fadeOut' // Animate.css class names: default:bounceOutRight
66 | }
67 | }).show()
68 | }
69 |
70 | /**
71 | * @function showNotification
72 | * @summary Shows a desktop notification
73 | * @description Shows a desktop notification
74 | * @param {string} message - The notification message text
75 | * @param {string} [title] - The title of the desktop notification
76 | */
77 | function showNotification (message, title = 'media-dupes') {
78 | const myNotification = new Notification(title, {
79 | body: message,
80 | icon: 'img/notification/icon.png'
81 | })
82 |
83 | myNotification.onclick = () => {
84 | writeConsoleMsg('info', 'showNotification ::: Notification clicked')
85 |
86 | const { ipcRenderer } = require('electron')
87 | ipcRenderer.send('showAndFocusMainUI') // tell main.js to show the main UI
88 | }
89 | }
90 |
91 | /**
92 | * @function openURL
93 | * @summary Opens an url in browser
94 | * @description Opens a given url in default browser. This is pretty slow, but got no better solution so far.
95 | * @param {string} url - URL string which contains the target url
96 | */
97 | function openURL (url) {
98 | const { shell } = require('electron')
99 | writeConsoleMsg('info', 'openURL ::: Trying to open the url: _' + url + '_.')
100 | shell.openExternal(url)
101 | }
102 |
103 | /**
104 | * @function validURL
105 | * @summary checks if a given string is a valid url
106 | * @description checks if a given string is a valid url
107 | * @param {string} -str - Given url
108 | * @return {boolean}
109 | */
110 | function validURL (str) {
111 | var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
112 | '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
113 | '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
114 | '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
115 | '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
116 | '(\\#[-a-z\\d_]*)?$', 'i') // fragment locator
117 | return !!pattern.test(str)
118 | }
119 |
120 | /**
121 | * @function formatBytes
122 | * @summary Calculate bytes to...
123 | * @description Calculate bytes to...
124 | * @param bytes - Incoming bytes value
125 | * @param decimals (optimal, defaults to 2)
126 | * @return Human readable value
127 | */
128 | function formatBytes (bytes, decimals = 2) {
129 | if (bytes === 0) return '0 Bytes'
130 |
131 | const k = 1024
132 | const dm = decimals < 0 ? 0 : decimals
133 | const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
134 | const i = Math.floor(Math.log(bytes) / Math.log(k))
135 |
136 | return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
137 | }
138 |
139 | /**
140 | * @function isEncoded
141 | * @summary Helper method for fullyDecodeURI
142 | * @description Helper method for fullyDecodeURI
143 | * @param {string} uri - the uri to check
144 | * @return {string} uri - the decoded uri
145 | */
146 | function isEncoded (uri) {
147 | uri = uri || ''
148 | return uri !== decodeURIComponent(uri)
149 | }
150 |
151 | /**
152 | * @function fullyDecodeURI
153 | * @summary Used to decode URLs
154 | * @description Used to decode URLs
155 | * param {string} uri - The incoming uri
156 | * @return {string} uri - a decoded url
157 | */
158 | function fullyDecodeURI (uri) {
159 | while (isEncoded(uri)) {
160 | uri = decodeURIComponent(uri)
161 | }
162 | return uri
163 | }
164 |
165 | /**
166 | * @function pathExists
167 | * @summary Checks if a given filepath exists
168 | * @description Checks if a given filepath exists using fs. Returns a boolean
169 | * param {string} path - The path which should be checked for existance
170 | * @return {boolean} -If path exists or not
171 | */
172 | function pathExists (path) {
173 | const fs = require('fs')
174 | if (fs.existsSync(path)) {
175 | return true // path exists
176 | } else {
177 | return false // path does not exists
178 | }
179 | }
180 |
181 | /**
182 | * @function globalObjectGet
183 | * @summary Gets a value of a single property from the global object in main.js
184 | * @description Gets a value of a single property from the global object in main.js
185 | * @param {String} property - Name of the property
186 | * @return {string} value - Value of the property
187 | */
188 | function globalObjectGet (property) {
189 | const { remote } = require('electron')
190 | var value = remote.getGlobal('sharedObj')[property]
191 | // writeConsoleMsg('info', 'globalObjectGet ::: Property: _' + property + '_ has the value: _' + value + '_.')
192 | return value
193 | }
194 |
195 | /**
196 | * @function globalObjectSet
197 | * @summary Updates the value of a single property from the global object in main.js
198 | * @description Updates the value of a single property from the global object in main.js
199 | * @param {String} property - Name of the property
200 | * @param {String} value - The new value of the property
201 | */
202 | function globalObjectSet (property, value) {
203 | const { ipcRenderer } = require('electron')
204 | ipcRenderer.send('globalObjectSet', property, value)
205 | }
206 |
207 | /**
208 | * @function isDirectoryAvailable
209 | * @summary Checks if a given directory exists
210 | * @description Checks if a given directory exists and returns a boolean
211 | * @param {string} dirPath - The directory path which should be checked
212 | * @return {boolean}
213 | */
214 | function isDirectoryAvailable (dirPath) {
215 | if (dirPath !== '') {
216 | const fs = require('fs')
217 | if (fs.existsSync(dirPath)) {
218 | writeConsoleMsg('info', 'isDirectoryAvailable ::: The directory _' + dirPath + '_ exists')
219 | return true
220 | } else {
221 | writeConsoleMsg('error', 'isDirectoryAvailable ::: The directory _' + dirPath + '_ does not exist')
222 | return false
223 | }
224 | } else {
225 | writeConsoleMsg('error', 'isDirectoryAvailable ::: Should check if a directory exists but the supplied parameter _' + dirPath + '_ was empty')
226 | }
227 | }
228 |
229 | /**
230 | * @function isDirectoryWriteable
231 | * @summary Checks if a given directory is writeable
232 | * @description Checks if a given directory is writeable and returns a boolean
233 | * @param {string} dirPath - The directory path which should be checked
234 | * @return {boolean}
235 | */
236 | function isDirectoryWriteable (dirPath) {
237 | if (dirPath !== '') {
238 | const fs = require('fs')
239 |
240 | // sync: check if folder is writeable
241 | try {
242 | fs.accessSync(dirPath, fs.constants.W_OK)
243 | writeConsoleMsg('info', 'isDirectoryWriteable ::: Directory _' + dirPath + '_ is writeable')
244 | return true
245 | } catch (err) {
246 | writeConsoleMsg('error', 'isDirectoryWriteable ::: Directory _' + dirPath + '_ is not writeable. Error: _' + err + '_.')
247 | return false
248 | }
249 | } else {
250 | writeConsoleMsg('error', 'isDirectoryWriteable ::: Should check if a directory is writeable but the supplied parameter _' + dirPath + '_ was empty.')
251 | }
252 | }
253 |
254 | function isFileWriteable (filePath) {
255 | if (filePath !== '') {
256 | const fs = require('fs')
257 |
258 | // sync: check if folder is writeable
259 | try {
260 | fs.accessSync(filePath, fs.constants.W_OK)
261 | writeConsoleMsg('info', 'isFileWriteable ::: File _' + filePath + '_ is writeable')
262 | return true
263 | } catch (err) {
264 | writeConsoleMsg('error', 'isFileWriteable ::: File _' + filePath + '_ is not writeable. Error: _' + err + '_.')
265 | return false
266 | }
267 | } else {
268 | writeConsoleMsg('error', 'isFileWriteable ::: Should check if a file is writeable but the supplied parameter _' + filePath + '_ was empty.')
269 | }
270 | }
271 |
272 | /**
273 | * @function userSettingWrite
274 | * @summary Write a user setting to file
275 | * @description Writes a value for a given key to electron-json-storage
276 | * @param {String} key - Name of storage key
277 | * @param {String} value - New value
278 | * @param {boolean} [silent] - If true - no notification is displayed on initial settingscreation
279 | * @throws Exception when writing a file failed
280 | */
281 | function userSettingWrite (key, value, silent = false) {
282 | const storage = require('electron-json-storage')
283 | const remote = require('electron').remote
284 | const app = remote.app
285 | const path = require('path')
286 |
287 | // set new path for userUsettings
288 | const userSettingsPath = path.join(app.getPath('userData'), 'UserSettings')
289 | storage.setDataPath(userSettingsPath)
290 |
291 | // write the user setting
292 | storage.set(key, { setting: value }, function (error) {
293 | if (error) {
294 | writeConsoleMsg('error', 'userSettingWrite ::: Unable to write setting _' + key + '_ = _' + value + '_. Error: ' + error)
295 | throw error
296 | }
297 | writeConsoleMsg('info', 'userSettingWrite ::: _' + key + '_ = _' + value + '_')
298 | globalObjectSet(key, value)
299 |
300 | if (silent === false) {
301 | showNoty('success', 'Set ' + key + ' to ' + value + ' .')
302 | }
303 | })
304 | }
305 |
306 | /**
307 | * @function userSettingRead
308 | * @summary Read a user setting from file
309 | * @description Reads a value stored in local storage (for a given key)
310 | * @param {String} key - Name of local storage key
311 | * @param {Boolean} [optionalUpdateSettingUI] Boolean used for an ugly hack
312 | */
313 | function userSettingRead (key, optionalUpdateSettingUI = false) {
314 | const storage = require('electron-json-storage')
315 | const remote = require('electron').remote
316 | const app = remote.app
317 | const path = require('path')
318 |
319 | // writeConsoleMsg('info', 'userSettingRead ::: Trying to read value of key: _' + key + '_.')
320 |
321 | // change path for userSettings
322 | const userSettingsPath = path.join(app.getPath('userData'), 'UserSettings')
323 | storage.setDataPath(userSettingsPath)
324 |
325 | // read the json file
326 | storage.get(key, function (error, data) {
327 | if (error) {
328 | writeConsoleMsg('error', 'userSettingRead ::: Unable to read user setting. Error: ' + error)
329 | throw error
330 | }
331 | var value = data.setting
332 | // writeConsoleMsg('info', 'userSettingRead ::: _' + key + '_ = _' + value + '_.')
333 |
334 | // Setting: enableVerboseMode
335 | //
336 | if (key === 'enableVerboseMode') {
337 | var settingVerboseMode
338 |
339 | // if it is not yet configured
340 | if ((value === null) || (value === undefined)) {
341 | settingVerboseMode = false // set the default default
342 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Initializing it now with the default value: _' + settingVerboseMode + '_.')
343 | userSettingWrite('enableVerboseMode', settingVerboseMode, true) // write the setting
344 | } else {
345 | settingVerboseMode = value // update global var
346 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingVerboseMode + '_.')
347 | }
348 | globalObjectSet('enableVerboseMode', settingVerboseMode) // update the global object
349 |
350 | // Optional: update the settings UI
351 | if (optionalUpdateSettingUI === true) {
352 | if (settingVerboseMode === true) {
353 | $('#checkboxEnableVerbose').prop('checked', true)
354 | } else {
355 | $('#checkboxEnableVerbose').prop('checked', false)
356 | }
357 | }
358 | }
359 | // end: enableVerboseMode
360 |
361 | // Setting: enableAdditionalParameter
362 | //
363 | if (key === 'enableAdditionalParameter') {
364 | var settingAdditionalParameter
365 |
366 | // if it is not yet configured
367 | if ((value === null) || (value === undefined)) {
368 | settingAdditionalParameter = false // set the default
369 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Initializing it now with the default value: _' + settingAdditionalParameter + '_.')
370 | userSettingWrite('enableAdditionalParameter', settingAdditionalParameter, true) // write the setting
371 | } else {
372 | settingAdditionalParameter = value // update global var
373 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingVerboseMode + '_.')
374 | }
375 | globalObjectSet('enableAdditionalParameter', settingAdditionalParameter) // update the global object
376 |
377 | // Optional: update the settings UI
378 | if (optionalUpdateSettingUI === true) {
379 | if (settingAdditionalParameter === true) {
380 | $('#checkboxEnableAdditionalParameter').prop('checked', true)
381 | } else {
382 | $('#checkboxEnableAdditionalParameter').prop('checked', false)
383 | }
384 | }
385 | }
386 | // end: enableAdditionalParameter
387 |
388 | // Setting: additonalYoutubeDlParameter
389 | //
390 | if (key === 'additionalYoutubeDlParameter') {
391 | var settingAdditionalDefinedParameter
392 |
393 | // if it is not yet configured
394 | if ((value === null) || (value === undefined)) {
395 | settingAdditionalDefinedParameter = '' // set the default
396 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Initializing it now with the default value: _' + settingAdditionalParameter + '_.')
397 | userSettingWrite('additionalYoutubeDlParameter', settingAdditionalDefinedParameter, true) // write the setting
398 | } else {
399 | settingAdditionalDefinedParameter = value // update global var
400 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingAdditionalDefinedParameter + '_.')
401 | }
402 | globalObjectSet('additionalYoutubeDlParameter', settingAdditionalDefinedParameter) // update the global object
403 |
404 | // Optional: update the settings UI
405 | if (optionalUpdateSettingUI === true) {
406 | $('#textInputAdditionalParameter').val(settingAdditionalDefinedParameter)
407 | }
408 | }
409 | // end: enableAdditionalParameter
410 |
411 | // Setting: enablePrereleases
412 | //
413 | if (key === 'enablePrereleases') {
414 | var settingPrereleases
415 |
416 | // if it is not yet configured
417 | if ((value === null) || (value === undefined)) {
418 | settingPrereleases = false // set the default
419 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Initializing it now with the default value: _' + settingPrereleases + '_.')
420 | userSettingWrite('enablePrereleases', settingPrereleases, true) // write the setting
421 | } else {
422 | settingPrereleases = value // update global var
423 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingPrereleases + '_.')
424 | }
425 | globalObjectSet('enablePrereleases', settingPrereleases) // update the global object
426 |
427 | // Optional: update the settings UI
428 | if (optionalUpdateSettingUI === true) {
429 | if (settingPrereleases === true) {
430 | $('#checkboxEnablePreReleases').prop('checked', true)
431 | } else {
432 | $('#checkboxEnablePreReleases').prop('checked', false)
433 | }
434 | }
435 | }
436 | // end: enablePrereleases
437 |
438 | // Setting: enableErrorReporting
439 | //
440 | if (key === 'enableErrorReporting') {
441 | var settingEnableErrorReporting
442 |
443 | // not configured
444 | if ((value === null) || (value === undefined)) {
445 | settingEnableErrorReporting = true
446 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Initializing it now with the default value: _' + settingEnableErrorReporting + '_.')
447 | userSettingWrite('enableErrorReporting', true, true) // write the setting
448 | sentry.enableSentry()
449 | } else {
450 | settingEnableErrorReporting = value
451 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingEnableErrorReporting + '_.')
452 |
453 | if (settingEnableErrorReporting === true) {
454 | sentry.enableSentry()
455 | } else {
456 | sentry.disableSentry()
457 | }
458 | }
459 | globalObjectSet('enableErrorReporting', settingEnableErrorReporting) // update the global object
460 |
461 | // Optional: update the settings UI
462 | if (optionalUpdateSettingUI === true) {
463 | if (settingEnableErrorReporting === true) {
464 | $('#checkboxEnableErrorReporting').prop('checked', true)
465 | } else {
466 | $('#checkboxEnableErrorReporting').prop('checked', false)
467 | }
468 | }
469 | }
470 | // end: enableErrorReporting
471 |
472 | // Setting: downloadDir
473 | //
474 | if (key === 'downloadDir') {
475 | // const { remote } = require('electron')
476 | var settingDownloadDir
477 |
478 | // not yet set - seems like initial run
479 | if ((value === null) || (value === undefined)) {
480 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Initial run - lets set the defaut dir.')
481 | var detectedDefaultDownloadDir = defaultDownloadFolderGet() // lets set it do the users default folder dir
482 | if (detectedDefaultDownloadDir[0]) {
483 | settingDownloadDir = detectedDefaultDownloadDir[1]
484 | userSettingWrite('downloadDir', settingDownloadDir, true)
485 | writeConsoleMsg('info', 'userSettingRead ::: _' + key + '_ got initial value: _' + settingDownloadDir + '_.')
486 | }
487 | } else {
488 | // there is a setting
489 | settingDownloadDir = value
490 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingDownloadDir + '_.')
491 |
492 | // check if directory exists
493 | if (isDirectoryAvailable(settingDownloadDir)) {
494 | // check if directory is writeable
495 | if (isDirectoryWriteable(settingDownloadDir)) {
496 | // dir is available and writeable - seems like everything is ok
497 | globalObjectSet('downloadDir', settingDownloadDir)
498 | } else {
499 | writeConsoleMsg('error', 'userSettingRead ::: Configured download dir _' + settingDownloadDir + '_ exists BUT is not writeable. Gonna reset the user-setting.')
500 | settingDownloadDir = ''
501 | globalObjectSet('downloadDir', settingDownloadDir)
502 |
503 | // delete the config
504 | storage.remove('downloadDir', function (error) {
505 | if (error) {
506 | writeConsoleMsg('error', 'userSettingRead ::: Unable to delete config. Error: ' + error)
507 | throw error
508 | }
509 | })
510 | }
511 | } else {
512 | // dir does not exists
513 | settingDownloadDir = ''
514 | writeConsoleMsg('error', 'userSettingRead ::: Configured download dir _' + settingDownloadDir + '_ does not exists. Gonna reset the user-setting.')
515 |
516 | settingDownloadDir = defaultDownloadFolderGet() // get the default download target
517 | globalObjectSet('downloadDir', settingDownloadDir) // update the object
518 | userSettingWrite('downloadDir', settingDownloadDir, true) // update the config
519 | }
520 |
521 | // Update UI select
522 | if (optionalUpdateSettingUI === true) {
523 | $('#inputCustomTargetDir').val(settingDownloadDir)
524 | }
525 | }
526 | writeConsoleMsg('info', 'userSettingRead ::: _' + key + '_ = _' + settingDownloadDir + '_.')
527 | }
528 | // end: downloadDir
529 |
530 | // Setting: audioFormat
531 | //
532 | if (key === 'audioFormat') {
533 | var settingAudioFormat
534 | // not configured
535 | if ((value === null) || (value === undefined)) {
536 | settingAudioFormat = 'mp3'
537 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Initializing it now with the default value: _' + settingAudioFormat + '_.')
538 | userSettingWrite('audioFormat', settingAudioFormat, true) // write the setting
539 | } else {
540 | settingAudioFormat = value
541 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingAudioFormat + '_.')
542 | globalObjectSet('audioFormat', settingAudioFormat)
543 | }
544 |
545 | // optional: Adjust the UI
546 | if (optionalUpdateSettingUI === true) {
547 | $('#inputGroupSelectAudio').val(settingAudioFormat) // Update UI select
548 | }
549 | }
550 | // end: audioFormat
551 |
552 | // Setting: confirmedDisclaimer
553 | //
554 | if (key === 'confirmedDisclaimer') {
555 | // special case - first check the global object - to make the -s startup parameter possible
556 | var skipDisclaimer = globalObjectGet('confirmedDisclaimer')
557 | if (skipDisclaimer === true) {
558 | // nothing to do here - we are skipping the disclaimer handling
559 | } else {
560 | var settingConfirmedDisclaimer
561 | // not configured
562 | if ((value === null) || (value === undefined)) {
563 | writeConsoleMsg('warn', 'userSettingRead ::: No user setting found for: _' + key + '_. Gonna show the disclaimer now')
564 | disclaimerShow()
565 | } else {
566 | settingConfirmedDisclaimer = true
567 | globalObjectSet('confirmedDisclaimer', settingConfirmedDisclaimer)
568 | writeConsoleMsg('info', 'userSettingRead ::: Found configured _' + key + '_ with value: _' + settingConfirmedDisclaimer + '_.')
569 | ui.windowMainBlurSet(false) // unblur the main UI
570 | }
571 | }
572 | }
573 | // end: confirmedDisclaimer
574 | })
575 | }
576 |
577 | /**
578 | * @function defaultDownloadFolderGet
579 | * @summary Validates if the default download directory of the user is useable.
580 | * @description Validates if the default download directory of the user is useable.
581 | * @retun {boolean} boolean - Is the folder useable
582 | * @return {String} defaultTargetPath - The path to the folder
583 | */
584 | function defaultDownloadFolderGet () {
585 | var defaultTargetPath = globalObjectGet('downloadDir') // use the default download target - which was configured in main.js
586 | writeConsoleMsg('info', 'defaultDownloadFolderGet ::: Searching the default download directory for this user ....')
587 | writeConsoleMsg('info', 'defaultDownloadFolderGet ::: Got' + defaultTargetPath + ' from global object')
588 |
589 | // check if that directory still exists
590 | if (isDirectoryAvailable(defaultTargetPath)) {
591 | writeConsoleMsg('info', 'defaultDownloadFolderGet ::: The default download location _' + defaultTargetPath + '_ exists') // the default download folder exists
592 |
593 | // check if it is writeable
594 | if (isDirectoryWriteable(defaultTargetPath)) {
595 | writeConsoleMsg('info', 'defaultDownloadFolderGet ::: The default download location _' + defaultTargetPath + '_ exists and is writeable. We are all good and gonna use it now')
596 | return [true, defaultTargetPath]
597 | } else {
598 | // folder exists but is not writeable
599 | writeConsoleMsg('error', 'defaultDownloadFolderGet ::: The default download location _' + defaultTargetPath + '_ exists but is not writeable. This is a major problem')
600 | showNoty('error', 'Your configured custom download directory ' + defaultTargetPath + ' exists but is not writeable. Gonna reset the custom setting now back to default', 0)
601 | return [false, '']
602 | }
603 | } else {
604 | // was unable to detect a download folder. Should force the user to set a custom one
605 | writeConsoleMsg('error', 'defaultDownloadFolderGet ::: Was unable to detect an existing default download location')
606 | showNoty('error', 'Unable to detect an existing default download location. Please configure a download directory in the application settings', 0)
607 | return [false, '']
608 | }
609 | }
610 |
611 | /**
612 | * @function disclaimerCheck
613 | * @summary Checks if the disclaimer should be shown or not
614 | * @description Is using userSettingRead() to read the user setting confirmedDisclaimer.json. If it exists the user previously confirmed it.
615 | */
616 | function disclaimerCheck () {
617 | userSettingRead('confirmedDisclaimer')
618 | }
619 |
620 | /**
621 | * @function disclaimerShow
622 | * @summary Opens the disclaimer as dialog
623 | * @description Displays a disclaimer regarding app usage. User should confirm it once. Setting is saved in UserSettings (.json file)
624 | */
625 | function disclaimerShow () {
626 | const dialog = require('electron').remote.dialog
627 | writeConsoleMsg('warn', 'disclaimerShow ::: Showing the disclaimer now.')
628 | var disclaimerTitle = 'media-dupes disclaimer'
629 | var disclaimerText = 'THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.'
630 |
631 | var choice = dialog.showMessageBoxSync(this,
632 | {
633 | type: 'info',
634 | buttons: ['Confirm'],
635 | title: disclaimerTitle,
636 | message: disclaimerText
637 | })
638 | if (choice === 0) {
639 | writeConsoleMsg('info', 'disclaimerShow ::: User confirmed the disclaimer.')
640 | userSettingWrite('confirmedDisclaimer', true)
641 | ui.windowMainBlurSet(false) // unblur the main UI
642 | }
643 | }
644 |
645 | /**
646 | * @function downloadStatusCheck
647 | * @summary Checks if all downloads finished
648 | * @description Checks if all downloads finished and creates a final status report using noty
649 | * @param {number} - overall - The amount of overall urls
650 | * @param {number} - succeeded - The amount of succeeded urls
651 | * @param {number} - failed - The amount of failed urls
652 | */
653 | function downloadStatusCheck (overall = 0, succeeded = 0, failed = 0) {
654 | var statusReport
655 | var notificationType
656 | var notificationTime
657 |
658 | writeConsoleMsg('info', 'downloadStatusCheck ::: Overall: ' + overall)
659 | writeConsoleMsg('info', 'downloadStatusCheck ::: Succeeded: ' + succeeded)
660 | writeConsoleMsg('warn', 'downloadStatusCheck ::: Failed: ' + failed)
661 |
662 | if (overall === succeeded + failed) {
663 | writeConsoleMsg('info', 'downloadStatusCheck ::: All download tasks are finished')
664 |
665 | // everything is fine
666 | if (overall === succeeded) {
667 | writeConsoleMsg('info', 'downloadStatusCheck ::: All downloads successfully')
668 | statusReport = 'Finished entire download queue (' + overall + ') successfully'
669 | notificationType = 'success'
670 | notificationTime = 3000
671 | }
672 |
673 | // some errors
674 | if ((overall === succeeded + failed) && (succeeded > 0) && (failed > 0)) {
675 | writeConsoleMsg('warn', 'downloadStatusCheck ::: Some downloads failed')
676 | statusReport = 'Finished entire download queue (' + overall + ') - ' + succeeded + ' succeeded and ' + failed + ' failed with errors.'
677 | notificationType = 'warning'
678 | notificationTime = 0
679 | }
680 |
681 | // all failed
682 | if ((overall === failed) && (succeeded === 0)) {
683 | writeConsoleMsg('error', 'downloadStatusCheck ::: All downloads failed')
684 | statusReport = 'Finished entire download queue (' + overall + ') - but all downloads failed with errors.'
685 | notificationType = 'error'
686 | notificationTime = 0
687 | }
688 |
689 | showNotification(statusReport) // show an OS notification
690 | showNoty(notificationType, statusReport, notificationTime) // show an in-app notification
691 | ui.windowMainLogAppend(statusReport + '', true) // append to log
692 | ui.windowMainLogAppend('### QUEUE ENDED ###\n\n', true) // Show mode in log
693 | ui.windowMainDownloadQueueFinished()
694 |
695 | // reset the datatable
696 | ui.dataTablesReset()
697 | } else {
698 | writeConsoleMsg('info', 'downloadStatusCheck ::: Some download tasks are not yet finished')
699 | }
700 | }
701 |
702 | /**
703 | * @function generateTimestamp
704 | * @summary Generates a timestamp
705 | * @description Generates a timestamp using time-stamp
706 | * @return {string} - timestamp - The generates timestamp
707 | */
708 | function generateTimestamp () {
709 | const timestamp = require('time-stamp')
710 | var currentTimestamp = timestamp('HH:mm:ss') // hours : minutes : seconds
711 | return currentTimestamp
712 | }
713 |
714 | /**
715 | * @function canWriteFileOrFolder
716 | * @summary Checks if a file or folder is writeable
717 | * @description Checks if a file or folder is writeable
718 | * @param {String} path - Path which should be checked
719 | */
720 | function canWriteFileOrFolder (path, callback) {
721 | const fs = require('fs')
722 | fs.access(path, fs.W_OK, function (err) {
723 | callback(null, !err)
724 | })
725 | }
726 |
727 | /**
728 | * @function generateUrlId
729 | * @summary generates an id based on a given url
730 | * @description generates an id based on a given url
731 | * @param {String} url - url
732 | * @return {string} urlId - the generated md5
733 | */
734 | function generateUrlId (url) {
735 | var md5 = require('md5')
736 | var urlId = md5(url)
737 | return urlId
738 | }
739 |
740 | // ----------------------------------------------------------------------------
741 | // EXPORT THE MODULE FUNCTIONS
742 | // ----------------------------------------------------------------------------
743 | //
744 | module.exports.writeConsoleMsg = writeConsoleMsg
745 | module.exports.showNoty = showNoty
746 | module.exports.showNotification = showNotification
747 | module.exports.openURL = openURL
748 | module.exports.validURL = validURL
749 | module.exports.formatBytes = formatBytes
750 | module.exports.isEncoded = isEncoded
751 | module.exports.fullyDecodeURI = fullyDecodeURI
752 | module.exports.pathExists = pathExists
753 | module.exports.globalObjectGet = globalObjectGet
754 | module.exports.globalObjectSet = globalObjectSet
755 | module.exports.isDirectoryAvailable = isDirectoryAvailable
756 | module.exports.isDirectoryWriteable = isDirectoryWriteable
757 | module.exports.isFileWriteable = isFileWriteable
758 | module.exports.userSettingWrite = userSettingWrite
759 | module.exports.userSettingRead = userSettingRead
760 | module.exports.defaultDownloadFolderGet = defaultDownloadFolderGet
761 | module.exports.disclaimerCheck = disclaimerCheck
762 | module.exports.disclaimerShow = disclaimerShow
763 | module.exports.downloadStatusCheck = downloadStatusCheck
764 | module.exports.generateTimestamp = generateTimestamp
765 | module.exports.canWriteFileOrFolder = canWriteFileOrFolder
766 | module.exports.generateUrlId = generateUrlId
767 |
--------------------------------------------------------------------------------