132 Commits

Author SHA1 Message Date
3deac4cda0 First commit of tdl
Initial round of re-branding, I temporarily am using the dta icon just
rotated 180, waiting for a permanent icon to use
Removed the initial create tab for dta but left any download links as is
Added webextension-polyfill-ts to enable retrieval of cookies
Removed all access and preferences to the download manager as it is not
relevant in this fork
2021-03-27 21:26:37 -07:00
49a589cb87 Add title and description to metalinks
Fixes #250
2020-08-30 00:29:26 +02:00
3c644e615d Random typos etc 2020-08-29 23:49:48 +02:00
c540c1fc29 Harden locale loader 2020-08-29 23:49:02 +02:00
3e31cb326b Provide additional extension storage db backend
Related #159
2020-08-29 22:58:08 +02:00
add5b65ff2 Code cleanups 2020-08-29 22:56:08 +02:00
6282f54ac0 Use localforage to work around private browsing issues
Related #159
2020-08-29 22:55:32 +02:00
1af01856e1 Provide MemoryDB fallback
Related to #159
2020-08-29 21:47:26 +02:00
3f650be613 Allow right clicks on menu items 2020-08-29 20:56:58 +02:00
0fa7738031 Update dependencies 2020-08-29 20:54:26 +02:00
ebd1581742 Make audio filter to apply to both, links and media 2020-08-29 19:59:57 +02:00
db1b50bc90 Update locales 2020-08-29 19:56:05 +02:00
8ad4d7a59d Update de locale (#225) 2020-08-29 19:23:38 +02:00
5c6cd47485 Create tr.json (#201) 2020-08-29 19:20:44 +02:00
dbd596e8ea New language: Swedish (#226) 2020-08-29 19:18:04 +02:00
0362eaf6e3 Update Czech locale (#236) 2020-08-29 19:15:59 +02:00
4538066e9d update typo messages.json of zh-CN (#260) 2020-08-29 19:15:15 +02:00
0da87398b4 Fix typo in windows.getLastFocused (#277) 2020-08-29 19:14:42 +02:00
82e7361567 Version 4.2.6 2019-11-26 13:13:09 +01:00
5c84493a0f build: cleanup stale bundles 2019-11-26 13:05:16 +01:00
83cb8e32f1 Update Readme.md 2019-11-25 19:14:51 +01:00
c49af54532 Remove compresslevel from build.py
Makes python3.6 choke
2019-11-25 19:08:33 +01:00
eee8c4ea1a Version 4.2.5 2019-11-21 15:07:36 +01:00
858b8f277e Update dev dependencies 2019-11-21 13:25:20 +01:00
6841fdcfc8 New icon 2019-11-21 13:25:20 +01:00
1e8e7ad6ec Use correct grouping 2019-11-21 13:21:02 +01:00
6ed84b9560 Move landing pages into own repo 2019-11-21 12:03:29 +01:00
f739cb789c Adjust some darkmode colors 2019-11-21 11:40:39 +01:00
0d470a7ce0 Handle CRLF in imports
Closes #186
2019-11-21 11:40:39 +01:00
9ec1d46787 Update of Danish translation (#185) 2019-11-21 11:21:47 +01:00
ba283e9221 Version 4.2.4 2019-10-28 11:28:16 +01:00
e549886532 Do not mess up queue when sorting in select
Closes #181
2019-10-28 11:13:19 +01:00
d3b7032229 Version 4.2.3 2019-10-13 13:44:38 +02:00
5586bcb671 Update changelog 2019-10-13 13:44:05 +02:00
bd72c417d2 Remove sounds on Opera
See #125
2019-10-13 13:44:05 +02:00
52643e0bec Update feature_request.md 2019-10-11 17:38:14 +02:00
af59fb60ff Improve bug report template. 2019-10-11 17:37:04 +02:00
ac2bc8cdfd Add privacy notice 2019-10-11 17:26:17 +02:00
c5309a8923 Always import links in the right context
Closes #163
2019-10-11 15:47:16 +02:00
1370723e6d Push raw indexes 2019-10-11 15:47:16 +02:00
4ba827fc15 Make sure we're on background 2019-10-11 15:47:16 +02:00
b2e20b9875 Update zh_CN translation (#161) 2019-10-11 13:39:36 +02:00
ef9cff003d Update ru translation
Closes #160
2019-10-11 13:37:21 +02:00
74b3ce7eb1 Dark mode: improve display of disabled menu items 2019-10-11 13:28:41 +02:00
6528e2118e Show context when no selection
Closes #162
2019-10-11 13:28:18 +02:00
c901438216 Link landing from changelog 2019-10-09 20:56:02 +02:00
7a0718d9cc Version 4.2.2 2019-10-09 19:13:13 +02:00
a7cc3c7fff Fix landing title 2019-10-09 18:59:45 +02:00
9d313f319d Use the correct full path in tooltips 2019-10-09 18:32:22 +02:00
856044c88c Open landing page on installs 2019-10-09 18:26:36 +02:00
de1b13a50f Add landing page 2019-10-09 18:10:40 +02:00
a981b7b8c7 Update nl locale
Closes #157
2019-10-08 22:24:32 +02:00
abe9d82d03 Always notify all observers 2019-10-08 22:24:32 +02:00
49a3f08a9a Update Arabic translation (#158) 2019-10-08 15:12:38 +02:00
afaa75fcdc Update Bulgarian localization (#156) 2019-10-08 15:12:14 +02:00
a5c749412a Work around Firefox window.create({left, top}) issues 2019-10-07 14:55:01 +02:00
e64da40355 Track window state of manager windows
Closes #154
2019-10-07 14:55:00 +02:00
05e7283f9f Update messages.json (#155) 2019-10-07 14:27:19 +02:00
bea8e230fb Update pl translation
Closes #151
2019-10-06 23:04:53 +02:00
23c1ece807 4 more French strings (#152) 2019-10-06 23:02:11 +02:00
539d340f1a Make prefwatcher not stop event chain
See #148
2019-10-06 22:33:23 +02:00
876486bbf5 Update hungarian translation (#149) 2019-10-06 19:34:58 +02:00
9179851c85 Update zh_TW translation (#147) 2019-10-06 08:44:31 +02:00
1e96d7e787 Version 4.2.1 Beta
For translators
2019-10-06 08:12:27 +02:00
612478bcc7 Add license to changelog 2019-10-06 08:12:00 +02:00
23f84fbde0 Use both .currentSrc and .src for image gathering
Works around some lazy-loader issues and make it at least as compatible as v3

Closes #145
2019-10-06 08:00:50 +02:00
b7b4c57034 Update de 2019-10-06 08:00:50 +02:00
872b058d4c Update et (#146) 2019-10-06 07:56:38 +02:00
93ad3e71db Update of lt locale (#144) 2019-10-05 22:39:52 +02:00
7d824bf61e Update Japanese translation 5th (#143) 2019-10-05 22:39:25 +02:00
06228d9ec9 Update messages.json (#141) 2019-10-03 17:04:27 +02:00
f9232ffd96 Update messages.json (#140) 2019-10-03 14:30:31 +02:00
ab3c335bf1 Polish pref window a bit 2019-10-03 09:04:00 +02:00
9142cc023f Add a basic editorconfig 2019-10-03 08:30:45 +02:00
3133a8d8ad Dark theme modals 2019-10-03 08:26:22 +02:00
312f39f7f6 Dark mode scrollbar colors 2019-10-02 22:09:04 +02:00
4ba7bb530d Close unhandled ports 2019-10-02 21:47:38 +02:00
9caad6b3a5 Detect browser theme on Firefox (#139) 2019-10-02 14:54:51 +02:00
5e323db2f0 Make theme configurable (#139) 2019-10-02 14:24:36 +02:00
18daa28cea Initial dark theme (#139) 2019-10-02 13:57:27 +02:00
65c358c01b Update Readme.md 2019-10-02 10:49:09 +02:00
2d14432efe Update TODO.md 2019-10-02 04:43:45 +02:00
19b1cc8856 Add Character batches
Closes #118
2019-10-02 04:37:19 +02:00
883f9a6f0b Add Changelog 2019-10-02 04:05:32 +02:00
e969ba237a Update packages 2019-10-02 04:04:39 +02:00
207248e706 Update ar translation (#90) 2019-10-02 01:55:26 +02:00
9925dec0f4 Use onDeterminingFilename when supported
Closes #132
2019-09-26 08:46:57 +02:00
09b2b4be10 4.1.2 2019-09-26 07:19:43 +02:00
f331de4134 Add Cmd+F shortcut to focus filter box 2019-09-26 07:08:26 +02:00
7760072e8e Avoid the filter box getting an outline in chrome 2019-09-26 07:02:12 +02:00
626bf592de List da locale in all.json 2019-09-26 06:38:49 +02:00
d550de0e27 Add Danish locale
Close #133
2019-09-26 06:37:49 +02:00
367efb4b53 Fix domain parsing in particular IPs
Closes #135
2019-09-26 06:34:23 +02:00
4a82c62958 Update zh_TW translation (#129) 2019-09-23 07:27:39 +02:00
891aa66bff Update zh_CN translation (#126) 2019-09-23 07:24:50 +02:00
2513b60c1b Update ko locale
Closes #127
2019-09-22 17:22:40 +02:00
31cca481f9 Update nl locale
Closes #128
2019-09-22 17:17:24 +02:00
31999bba9f Version 4.1.1 2019-09-21 09:48:56 +02:00
73d90662e3 Remove debug statement 2019-09-21 09:44:38 +02:00
5f5deb09f3 Add audios to DOM to avoid some playback errors 2019-09-21 09:41:40 +02:00
b68245f4f0 Do not treat CRASH as recoverable 2019-09-21 09:34:27 +02:00
34c8537cb5 Update locale list 2019-09-21 09:26:59 +02:00
6b7c9d461d Update ru translation 2019-09-21 09:26:01 +02:00
d2f09ca592 Added Bulgarian localization (#124) 2019-09-21 09:20:55 +02:00
919c6a8f10 more French strings (#120) 2019-09-19 01:52:30 +02:00
dcd7e7cd0e Update pl translation 2019-09-17 13:46:45 +02:00
c545bbab1d Update de locale 2019-09-17 13:46:45 +02:00
dc6e64e690 Add language specific donate URLs 2019-09-17 13:46:45 +02:00
578762db27 Update of lt locale (#117) 2019-09-17 13:39:41 +02:00
e996a2b41a Indonesia translation update (#115) 2019-09-17 03:43:56 +02:00
38496d9161 Hungarian translation update (#114) 2019-09-17 01:17:14 +02:00
4a0756aa26 Updated Spanish translation (#113) 2019-09-16 19:24:57 +02:00
63d0ff22fa Update Italian translation (#112)
Revised and updated translation, which contains work from me and @harbons
2019-09-16 18:31:17 +02:00
5390642978 Update Estonian translation (#111) 2019-09-16 18:20:11 +02:00
2e59dedda3 Japanese translation 4th (#110) 2019-09-16 18:19:33 +02:00
ee649717a2 Update Portuguese translation (#108) 2019-09-16 18:18:41 +02:00
078ce277ce Fix default subfolder suggestion.
Closes #109
2019-09-16 08:12:34 +02:00
7a3cad83b0 Japanese Translation 3rd (#107) 2019-09-15 19:12:53 +02:00
4d953c373f Basic import/export
Closes #64
2019-09-15 12:28:31 +02:00
da9832552f Update Greek translation
Closes #103
2019-09-15 09:51:14 +02:00
5909633a04 Updated Spanish translation (#101) 2019-09-15 09:45:19 +02:00
750fd987bd Update French translation
Closes #102
2019-09-15 09:43:01 +02:00
b87e0d6138 Japanese Translation 2nd (#100) 2019-09-14 21:33:40 +02:00
31cb23923a Add a subfolders dropdown
The dropdown will help users switch between folders easier than using the mask.
Also, the dropdown is more discoverable than masked folders.

See #2
2019-09-14 01:07:29 +02:00
71d98bc603 Use correct rows for tooltip ETA/status 2019-09-14 00:00:20 +02:00
4b09a0db67 Retries
Closes #95
2019-09-14 00:00:11 +02:00
da6c6bcf68 User Canceled error message 2019-09-13 23:09:44 +02:00
84fea3ba35 Allow to open Manager in a popup
Closes #47
2019-09-13 23:09:44 +02:00
33de1cbce9 Allow setting the referrer in select
Closes #78
2019-09-13 23:09:43 +02:00
04b8a981ef Allow to choose button action
Closes #51
2019-09-13 23:09:43 +02:00
58c7955c64 Add Delete Files action
Closes #92
2019-09-13 23:09:43 +02:00
dcf9603da8 Play done sound
Cloes #81
2019-09-13 23:09:43 +02:00
113 changed files with 16825 additions and 7439 deletions

View File

@ -1,6 +1,6 @@
---
name: Bug report
about: Create a report to help us improve
about: Create a report to help us improve DownThemAll!
title: ''
labels: ''
assignees: ''
@ -8,9 +8,9 @@ assignees: ''
---
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
- OS: [e.g. Windows 10, macOS, Linux (distribution, desktop environment)]
- Browser and version: [e.g. Firefox 42, Chrome 70, Opera 15, Seamonkey 2.16]
- DownThemAll! version: [e.g. 4.2, 3.0, latest]
**Describe the bug**
A clear and concise description of what the bug is.

View File

@ -1,6 +1,6 @@
---
name: Feature request
about: Suggest an idea for this project
about: Suggest a feature or an idea for DownThemAll!
title: ''
labels: ''
assignees: ''

View File

@ -1,3 +1,7 @@
![DownThemAll!](https://raw.githubusercontent.com/downthemall/downthemall/master/style/icon128.png)
# DownThemAll! WE
The DownThemAll! WebExtension.
@ -27,28 +31,59 @@ If you would like to help out translating DTA, please see our [translation guide
## Development
### Requirements
You will want to `yarn` the development dependencies such as webpack first.
- [node](https://nodejs.org/en/)
- [yarn](https://yarnpkg.com/)
- [python3](https://www.python.org/) >= 3.6 (to build zips)
- [web-ext](https://www.npmjs.com/package/web-ext) (for development ease)
Afterwards, you will want to run`yarn watch`.
### Setup
You will want to run `yarn` to install the development dependencies such as webpack first.
### Making changes
Just use your favorite text editor to edit the files.
You will want to run`yarn watch`.
This will run the webpack bundler in watch mode, transpiling the TypeScript to Javascript and updating bundles as you change the source.
Please note: You have to run `yarn watch` (at least once) as it builds the actual script bundles.
Please note: You have to run `yarn watch` or `yarn build` (at least once) as it builds the actual script bundles.
### Firefox
### Running in Firefox
I recommend you install the [`web-ext`](https://www.npmjs.com/package/web-ext) tools from mozilla. It is not listed as a dependency by design at it causes problems with dependency resolution in yarn right now if installed in the same location as the rest of the dependencies.
If you did, then running `yarn webext` (additionally to `yarn watch`) will run the WebExtension in a development profile. This will use the directory `../dtalite.p` to keep a development profile. You might need to create this directory before you use this command. Furthermore `yarn webext` will watch for changes to the sources and automatically reload the extension.
Alternative, you can also `yarn build`, which then builds an *unsigned* zip that you can then install permanently in a browser that does not enforce signing (i.e. Nightly or the Unbranded Firefox with the right about:config preferences).
Alternatively, you can also `yarn build`, which then builds an *unsigned* zip that you can then install permanently in a browser that does not enforce signing (i.e. Nightly or the Unbranded Firefox with the right about:config preferences).
### Chrome
### Running in Chrome/Chromium/etc
You have to build the bundles first, of course.
Then put your Chrome into Developement Mode on the Extensions page, and Load Unpacked the directory of your downthemall clone.
### Making release zips
To get a basic unofficial set of zips for Firefox and chrome, run `yarn build`.
If you want to generate release builds like the ones that are eventually released in the extension stores, use `python3 util/build.py --mode=release`.
The output is located in `web-ext-artifacts`.
- `-fx.zip` are Firefox builds
- `-crx.zip` are Chrome/Chromium builds
- `-opr.zip` are Opera builds (essentially like the Chrome one, but without sounds)
### The AMO Editors tl;dr guide
1. Install the requirements.
2. `yarn && python3 build/util.py --mode=release`
3. Have a look in `web-ext-artifacts/dta-*-fx.zip`
### Patches
Before submitting patches, please make sure you run eslint (if this isn't done automatically in your text editor/IDE), and eslint does not report any open issues. Code contributions should favor typescript code over javascript code. External dependencies that would ship with the final product (including all npm/yarn packages) should be kept to a bare minimum and need justification.

11
TODO.md
View File

@ -1,16 +1,11 @@
TODO
---
aka a lot
P2
===
Planned for later.
* Soft errors and retry logic
* Big caveat: When the server still responds, like 50x errors which would be recoverable, we actually have no way of knowing it did in respond in such a way. See P4 - Handle Errors remarks.
* Delete files (well, as far as the browser allows)
* Inter-addon API (basic)
* Add downloads
* vtable perf: cache column widths
@ -29,11 +24,7 @@ Nice-to-haves.
* Manipulate downloads (e.g. rewrite URLs)
* Native context menus?
* Would require massive reworks incl the need for new icon formats, but potentially feasible.
* Import/Export
* Download priorities (manual scheduling overrides)
* Dark Theme support
* os/browser define be default
* overwritable
* Remove `any` types as possible, and generally improve typescript (new language to me)
P4
@ -45,8 +36,6 @@ Stuff that probably cannot be implemented due to WeberEension limitations.
* Firefox helpfully keeps different lists of downloads. One for newly added downloads, and other ones for "previous" downloads. Turns out the WebExtension API only ever queries the "new" list.
* Segmented downloads
* Cannot be done with WebExtensions - downloads API has no support and manually downloading, storing in temporary add-on storage and reassmbling the downloaded parts later is not only efficient but does not reliabliy work due to storage limitations.
* Handle errors, 404 and such
* The Firefox download manager is too stupid and webRequest does not see Downloads, so cannot be done right now.
* Conflicts: ask when a file exists
* Not supported by Firefox
* Speed limiter

View File

@ -1,11 +1,13 @@
{
"ar": "العربية [ar]",
"bg": "Български [bg]",
"cs": "Čeština (CZ) [cs]",
"da": "Dansk [da]",
"de": "Deutsch [de]",
"el": "Ελληνικά [el]",
"en": "English (US) [en]",
"es": "Español (España) [es]",
"et": "Eesti Keel [et]",
"et": "Eesti keel [et]",
"fr": "Français [fr]",
"hu": "Magyar (HU) [hu]",
"id": "Bahasa Indonesia [id]",
@ -17,6 +19,8 @@
"pl": "Polski [pl]",
"pt": "Português (Brasil) [pt]",
"ru": "Русский [ru]",
"zh_CN": "简体中文 [zh_CN]",
"zh_TW": "正體中文 [zh_TW]"
"sv": "Svenska (SV) [sv]",
"tr": "Türkçe TR) [tr]",
"zh_CN": "中文(简体) [zh_CN]",
"zh_TW": "正體中文 (TW) [zh_TW]"
}

View File

@ -191,6 +191,22 @@
"message": "احذف",
"description": "button text"
},
"deletefiles": {
"message": "احذف الملفات",
"description": "menu action"
},
"deletefiles_button": {
"message": "احذف",
"description": "button text"
},
"deletefiles_text": {
"message": "أتريد إزالة الملفات الآتية؟",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "حذف الملفات",
"description": "messagebox title"
},
"description": {
"message": "الوصف",
"description": "Description (keep it short); e.g. the description column in select"
@ -275,13 +291,37 @@
"message": "العنوان غير صالح",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "لا تدعم المتصفّحات أي مسار مُطلق للمجلدات الفرعية",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "لا تدعم المتصفّحات النقط (.) في المجلدات الفرعية",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "لم تحدّد عناصر",
"description": "Error Message; select window"
},
"export": {
"message": "صدّر إلى ملف",
"description": "menu text"
},
"export_aria2": {
"message": "صدّر بنسق قائمة aria2",
"description": "menu text"
},
"export_metalink": {
"message": "صدّر بنسق Metalink",
"description": "menu text"
},
"export_text": {
"message": "صدّر كنصّ",
"description": "menu text"
},
"extensionDescription": {
"message": "أداة التنزيل لبحور من الروابط، داخل متصفحك",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "ترشيح سريع",
@ -331,6 +371,10 @@
"message": "يُجبر البدء",
"description": "Menu text"
},
"import": {
"message": "استورِد من نصّ",
"description": "menu text"
},
"information_title": {
"message": "معلومات",
"description": "Used in message boxes"
@ -529,6 +573,26 @@
"message": "أضفِ التنزيلات الجديدة ملبثة، بدل بدئها مباشرة عند إضافتها",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "زر ”نزّلها كلها!“:",
"description": "label"
},
"pref_button_type_dta": {
"message": "تحديد ”نزّلها كلها“",
"description": "label"
},
"pref_button_type_manager": {
"message": "يفتح المدير",
"description": "label"
},
"pref_button_type_popup": {
"message": "يعرض القائمة",
"description": "label"
},
"pref_button_type_turbo": {
"message": "ينفّذ ”بنقرة واحدة!“",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "التنزيلات المتزامنة",
"description": "Preferences/Network"
@ -537,10 +601,6 @@
"message": "اعرض إخطارًا حين ينتهي تنزيل طابور التنزيلات",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "ليكن زر المتصفح ”بنقرة واحدة!“",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "لا تعرض عناصر القائمة السياقية العامة",
"description": "Preferences/General"
@ -549,6 +609,10 @@
"message": "المدير",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "افتح المدير في نافذة منبثقة جديدة",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "اعرض التلميحات في ألسنة المدير",
"description": "Preferences/General"
@ -573,14 +637,42 @@
"message": "أزِل التنزيلات الناقصة بعد إعادة التشغيل",
"description": "Preferences/General"
},
"pref_retries": {
"message": "عدد مرات إعادة التنزيل في حال الأخطاء المؤقتة",
"description": "pref text"
},
"pref_retry_time": {
"message": "أعِد كل (بالدقيقة)",
"description": "pref text"
},
"pref_show_urls": {
"message": "اعرض العناوين بدل الأسماء",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "شغّل الأصوات",
"description": "checkbox text"
},
"pref_text_links": {
"message": "حاوِل البحث عن الروابط في نص موقع الوِب (أبطأ)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "السمة:",
"description": "label text"
},
"pref_theme_dark": {
"message": "داكنة",
"description": "option text"
},
"pref_theme_default": {
"message": "النظام/المتصفح",
"description": "option text"
},
"pref_theme_light": {
"message": "فاتحة",
"description": "option text"
},
"pref_ui": {
"message": "واجهة المستخدم",
"description": "Preferences/General; group text"
@ -895,6 +987,20 @@
"message": "استأنف",
"description": "Action for resuming a download"
},
"retrying": {
"message": "يُعيد",
"description": "Status text"
},
"retrying_error": {
"message": "يُعيد -$ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "يعمل",
"description": "Status text"
@ -943,6 +1049,18 @@
"message": "اضبط قناع تغيير الاسم",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "اضبط قناع تغيير اسم جديد",
"description": "dialog text"
},
"set_referrer": {
"message": "اضبط المُعيد",
"description": "menu text"
},
"set_referrer_text": {
"message": "اضبط مُعيدًا جديدًا",
"description": "dialog text"
},
"single_batchexamples": {
"message": "الدفعات مدعومة، مثل:",
"description": "Header text; single window"
@ -1123,6 +1241,14 @@
"message": "لن تبدأ أي تنزيلات جديدة",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "المجلد الفرعي:",
"description": "label text"
},
"subfolder_placeholder": {
"message": "ضَع الملفات في هذا المجلد الفرعي لدليل المجلدات في الجهاز",
"description": "placeholder text within an input box"
},
"title": {
"message": "العنوان",
"description": "Column text; Title label (short)"
@ -1166,5 +1292,9 @@
"useonlyonce": {
"message": "استعمله مرة واحدة",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "ألغاها المستخدم",
"description": "Error message"
}
}

1300
_locales/bg/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -191,6 +191,22 @@
"message": "Smazat",
"description": "button text"
},
"deletefiles": {
"message": "Odstranit soubory",
"description": "menu action"
},
"deletefiles_button": {
"message": "Odstranit",
"description": "button text"
},
"deletefiles_text": {
"message": "Opravdu chcete odstranit následující soubory?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "Odstranit soubory",
"description": "messagebox title"
},
"description": {
"message": "Popis",
"description": "Description (keep it short); e.g. the description column in select"
@ -216,27 +232,27 @@
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular": {
"message": "DownThemAll!",
"message": "TraitorousDownloading!",
"description": "Regular dta action; Menu text"
},
"dta_regular_all": {
"message": "DownThemAll! - Všechny záložky",
"message": "TraitorousDownloading! - Všechny záložky",
"description": "Menu text"
},
"dta_regular_image": {
"message": "Stáhnout obrázek pomocí DownThemAll!",
"message": "Stáhnout obrázek pomocí TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_link": {
"message": "Stáhnout odkaz pomocí DownThemAll!",
"message": "Stáhnout odkaz pomocí TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_media": {
"message": "Stáhnout média pomocí DownThemAll!",
"message": "Stáhnout média pomocí TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_selection": {
"message": "Stáhnout vybrané pomocí DownThemAll!",
"message": "Stáhnout vybrané pomocí TraitorousDownloading!",
"description": "Menu text"
},
"dta_turbo": {
@ -275,13 +291,37 @@
"message": "Neplatná URL adresa",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "Absolutní formát cest pro podsložky není prohlížečem podporovaný",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "Tečky (.) v podsložkách nejsou prohlížečem podporované",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "Nic není vybráno",
"description": "Error Message; select window"
},
"export": {
"message": "Exportovat do souboru",
"description": "menu text"
},
"export_aria2": {
"message": "Exportovat jako seznam aria2",
"description": "menu text"
},
"export_metalink": {
"message": "Exportovat jako metalink",
"description": "menu text"
},
"export_text": {
"message": "Exportovat jako text",
"description": "menu text"
},
"extensionDescription": {
"message": "Správce stahování pro Váš prohlížeč",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "Rychlé filtrování",
@ -331,6 +371,10 @@
"message": "Vynutit spuštění",
"description": "Menu text"
},
"import": {
"message": "Importovat ze souboru",
"description": "menu text"
},
"information_title": {
"message": "Informace",
"description": "Used in message boxes"
@ -406,7 +450,7 @@
}
},
"manager_title": {
"message": "DownThemAll! Manažer",
"message": "TraitorousDownloading! Manažer",
"description": "Window/tab title"
},
"mask": {
@ -442,7 +486,7 @@
"description": "Action for moving a download up"
},
"nagging_message": {
"message": "Již bylo stáhnuto $DOWNLOADS$ souborů pomocí DownThemAll! Jako častý uživatel byste mohl/a podpořit další vývoj darem. Děkujeme!",
"message": "Již bylo stáhnuto $DOWNLOADS$ souborů pomocí TraitorousDownloading! Jako častý uživatel byste mohl/a podpořit další vývoj darem. Děkujeme!",
"description": "Donation nagging message; displayed as a notification bar in manager",
"placeholders": {
"downloads": {
@ -522,13 +566,33 @@
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "Nastavení DownThemAll!",
"message": "Nastavení TraitorousDownloading!",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "Přidávat nová stahování pozastavená",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "Tlačítko TraitorousDownloading!:",
"description": "label"
},
"pref_button_type_dta": {
"message": "TraitorousDownloading! selektor",
"description": "label"
},
"pref_button_type_manager": {
"message": "Otevřít Manažera",
"description": "label"
},
"pref_button_type_popup": {
"message": "Nabídka",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "Souběžná stahování",
"description": "Preferences/Network"
@ -537,10 +601,6 @@
"message": "Zobrazit oznámení po dokončení fronty stahování",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "Tlačítho na liště je OneClick!",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "Nezobrazovat v kontextové nabídce",
"description": "Preferences/General"
@ -549,8 +609,12 @@
"message": "Manažer",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "Otevřít Manažera v novém vyskakovacím okně",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "Zobrazovat popisky v záložkách Manažeru",
"message": "Zobrazovat popisky v záložkách Manažera",
"description": "Preferences/General"
},
"pref_netglobal": {
@ -558,7 +622,7 @@
"description": "Preferences/General; group text"
},
"pref_open_manager_on_queue": {
"message": "Otevřít záložku Manažeru po zařazení stahování do fronty",
"message": "Otevřít záložku Manažera po zařazení stahování do fronty",
"description": "Preferences/General"
},
"pref_queueing": {
@ -573,14 +637,42 @@
"message": "Odebrat chybějící stahování po restartu",
"description": "Preferences/General"
},
"pref_retries": {
"message": "Počet pokusů o stažení při dočasných chybách",
"description": "pref text"
},
"pref_retry_time": {
"message": "Interval opakování (v minutách)",
"description": "pref text"
},
"pref_show_urls": {
"message": "Zobrazit URL adresy místo názvů souborů",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "Přehrát zvuky",
"description": "checkbox text"
},
"pref_text_links": {
"message": "Vyhledat odkazy v textu stránky (pomalejší)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Motiv:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Tmavý",
"description": "option text"
},
"pref_theme_default": {
"message": "Systémový/Výchozí",
"description": "option text"
},
"pref_theme_light": {
"message": "Světlý",
"description": "option text"
},
"pref_ui": {
"message": "Uživatelské rozhraní",
"description": "Preferences/General; group text"
@ -895,6 +987,20 @@
"message": "Pokračovat",
"description": "Action for resuming a download"
},
"retrying": {
"message": "Opakuji",
"description": "Status text"
},
"retrying_error": {
"message": "Opakuji - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "Probíhá",
"description": "Status text"
@ -920,7 +1026,7 @@
"description": "Menu text; select context"
},
"select_title": {
"message": "DownThemAll! - Vyberte soubory ke stažení",
"message": "TraitorousDownloading! - Vyberte soubory ke stažení",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
@ -943,6 +1049,18 @@
"message": "Nastavit masku přejmenování",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "Nastavte novou masku přejmenování",
"description": "dialog text"
},
"set_referrer": {
"message": "Nastavit odkázání",
"description": "menu text"
},
"set_referrer_text": {
"message": "Nastavit novou odkazující stránku",
"description": "dialog text"
},
"single_batchexamples": {
"message": "Jsou podporována dávková stahování, například:",
"description": "Header text; single window"
@ -952,7 +1070,7 @@
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - Přidat odkaz",
"message": "TraitorousDownloading! - Přidat odkaz",
"description": "Title of single window"
},
"sizeB": {
@ -1123,6 +1241,14 @@
"message": "Nová stahování nebudou spuštěna",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "Podsložka:",
"description": "label text"
},
"subfolder_placeholder": {
"message": "Umístí soubory v této podložce uvnitř vaší složky stahování",
"description": "placeholder text within an input box"
},
"title": {
"message": "Popisek",
"description": "Column text; Title label (short)"
@ -1166,5 +1292,9 @@
"useonlyonce": {
"message": "Použít pouze jednou",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "Zrušeno uživatelem",
"description": "Error message"
}
}

1300
_locales/da/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"language": {
"message": "Deutsch",
"description": "Lanuage Name in your language"
"description": "Language Name in your language"
},
"language_code": {
"message": "de",
@ -24,7 +24,7 @@
"description": "Checkbox label"
},
"add_paused_question": {
"message": "Soll sich diese Entscheidung gemerkt werden und neue Downloads von jetzt an immer pausiert hinzugefügt werden?",
"message": "Diese Entscheidung speichern und neue Downloads von jetzt an immer pausiert hinzufügen?",
"description": "Messagebox text"
},
"add_paused_title": {
@ -52,15 +52,15 @@
"description": "Messagebox info text for batch confirmations"
},
"batch_question": {
"message": "Als Batch oder einfachen Download hinzufügen?",
"message": "Als Batch oder als einfachen Download hinzufügen?",
"description": "Messagebox info text for batch confirmations"
},
"batch_single": {
"message": "Einfacher Download",
"message": "Einzelner Download",
"description": "Button text for batch confirmation"
},
"batch_title": {
"message": "Batch Download",
"message": "Batch",
"description": "Messagebox title for batch confirmations"
},
"cancel": {
@ -84,7 +84,7 @@
"description": "Checkbox label text for decision confirmations"
},
"check_selected_items": {
"message": "Ausgewählte Einträge markieren",
"message": "markierte Einträge auswählen",
"description": "Menu text"
},
"colConnections": {
@ -191,6 +191,22 @@
"message": "Entfernen",
"description": "button text"
},
"deletefiles": {
"message": "Dateien löschen",
"description": "menu action"
},
"deletefiles_button": {
"message": "Löschen",
"description": "button text"
},
"deletefiles_text": {
"message": "Wirklich die folgenden Dateien löschen?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "Dateien Löschen",
"description": "messagebox title"
},
"description": {
"message": "Beschreibung",
"description": "Description (keep it short); e.g. the description column in select"
@ -212,31 +228,31 @@
"description": "Download (noun); e.g. Download column in select"
},
"download_verb": {
"message": "Download",
"message": "Herunterladen",
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular": {
"message": "DownThemAll!",
"message": "TraitorousDownloading!",
"description": "Regular dta action; Menu text"
},
"dta_regular_all": {
"message": "DownThemAll! - Alle Tabs",
"message": "TraitorousDownloading! - Alle Tabs",
"description": "Menu text"
},
"dta_regular_image": {
"message": "Bild mit DownThemAll! speichern",
"message": "Bild mit TraitorousDownloading! speichern",
"description": "Menu text"
},
"dta_regular_link": {
"message": "Link mit DownThemAll! speichern",
"message": "Link mit TraitorousDownloading! speichern",
"description": "Menu text"
},
"dta_regular_media": {
"message": "Medium mit DownThemAll! speichern",
"message": "Medium mit TraitorousDownloading! speichern",
"description": "Menu text"
},
"dta_regular_selection": {
"message": "Auswahl mit DownThemAll! speichern",
"message": "Auswahl mit TraitorousDownloading! speichern",
"description": "Menu text"
},
"dta_turbo": {
@ -275,20 +291,44 @@
"message": "Ungültige URL",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "Absolute Pfade für Unterverzeichnisse sind durch Browser nicht unterstützt",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "Punkte (.) in Unterverzeichnissen sind durch Browser nicht unterstützt",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "Nichts ausgewählt",
"description": "Error Message; select window"
},
"export": {
"message": "In Datei exportieren",
"description": "menu text"
},
"export_aria2": {
"message": "Als aria2 Liste exportieren",
"description": "menu text"
},
"export_metalink": {
"message": "Als Metalink exportieren",
"description": "menu text"
},
"export_text": {
"message": "Als Text exportieren",
"description": "menu text"
},
"extensionDescription": {
"message": "Der Massen-Downloader für Deinen Browser",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "Schnelles Filtern",
"description": "Label for Fast Filtering input"
},
"fastfilter_placeholder": {
"message": "Platzhalter-Ausdruck oder Regular Expression",
"message": "Platzhalter-Ausdruck oder regulärer Ausdruck",
"description": "Placeholder for fastfilter inputs"
},
"FILE_FAILED": {
@ -308,11 +348,11 @@
"description": "Message box label"
},
"filter_label": {
"message": "Filter-Titel",
"message": "Filter-Bezeichnung",
"description": "Message box label"
},
"filter_types": {
"message": "Filter Typen",
"message": "Filter-Typen",
"description": "Message box label"
},
"filter_type_link": {
@ -320,17 +360,21 @@
"description": "Message box checkbox label"
},
"filter_type_media": {
"message": "Medien Filter",
"message": "Medien-Filter",
"description": "Message box checkbox label"
},
"finishing": {
"message": "Beenden",
"message": "Beende",
"description": "Status text"
},
"force_start": {
"message": "Start erzwingen",
"description": "Menu text"
},
"import": {
"message": "Aus Datei importieren",
"description": "menu text"
},
"information_title": {
"message": "Information",
"description": "Used in message boxes"
@ -372,7 +416,7 @@
"description": "Short for PageUp-key"
},
"limited_to": {
"message": "Begrenzt auf",
"message": "Begrenzen auf",
"description": "Label text; used in prefs/network"
},
"links": {
@ -406,7 +450,7 @@
}
},
"manager_title": {
"message": "DownThemAll! Manager",
"message": "TraitorousDownloading! Manager",
"description": "Window/tab title"
},
"mask": {
@ -442,7 +486,7 @@
"description": "Action for moving a download up"
},
"nagging_message": {
"message": "Du hast bisher $DOWNLOADS$ Downloads mit DownThemAll! hinzugefügt. Als regelmäßiger Benutzer können Sie eine Spende in Betracht ziehen um die Weiterentwicklung zu unterstützen. Danke!",
"message": "Sie haben bisher $DOWNLOADS$ Downloads mit TraitorousDownloading! hinzugefügt. Als regelmäßiger Benutzer können Sie eine Spende in Betracht ziehen um die Weiterentwicklung zu unterstützen. Danke!",
"description": "Donation nagging message; displayed as a notification bar in manager",
"placeholders": {
"downloads": {
@ -522,13 +566,33 @@
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "DownThemAll! Einstellungen",
"message": "TraitorousDownloading! Einstellungen",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "Neue Downloads immer pausiert hinzufügen, anstatt sie direkt zu starten",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "TraitorousDownloading! Button:",
"description": "label"
},
"pref_button_type_dta": {
"message": "TraitorousDownloading! Auswahl",
"description": "label"
},
"pref_button_type_manager": {
"message": "Manager öffnen",
"description": "label"
},
"pref_button_type_popup": {
"message": "Popup-Menü",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "Gleichzeitige Downloads",
"description": "Preferences/Network"
@ -537,10 +601,6 @@
"message": "Benachrichtigung anzeigen, wenn die Warteschlange fertig heruntergeladen ist",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "Browser Button soll OneClick! sein",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "Keine allgemeinen Menü-Eintrage anzeigen",
"description": "Preferences/General"
@ -549,8 +609,12 @@
"message": "Manager",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "Manager in neuem Fenster öffnen",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "Keine Tooltips im Manager-Tab anzeigen",
"message": "Tooltips im Manager-Tab anzeigen",
"description": "Preferences/General"
},
"pref_netglobal": {
@ -558,7 +622,7 @@
"description": "Preferences/General; group text"
},
"pref_open_manager_on_queue": {
"message": "Den Manager öffnen nachdem neue Downloads zur Warteschlange hinzugefügt wurden",
"message": "Den Manager öffnen, nachdem neue Downloads zur Warteschlange hinzugefügt wurden",
"description": "Preferences/General"
},
"pref_queueing": {
@ -573,14 +637,42 @@
"message": "Fehlende Downloads nach einem Neustart automatisch entfernen",
"description": "Preferences/General"
},
"pref_retries": {
"message": "Anzahl der Neuversuche bei vorübergehenden Fehlern",
"description": "pref text"
},
"pref_retry_time": {
"message": "Erneut versuchen alle (in Minuten)",
"description": "pref text"
},
"pref_show_urls": {
"message": "URLs anstelle von Namen anzeigen",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "Töne abspielen",
"description": "checkbox text"
},
"pref_text_links": {
"message": "Versuche Text-Links in Webseiten zu finden (langsamer)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Farbschema:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Dunkel",
"description": "option text"
},
"pref_theme_default": {
"message": "System/Browser",
"description": "option text"
},
"pref_theme_light": {
"message": "Hell",
"description": "option text"
},
"pref_ui": {
"message": "Benutzeroberfläche",
"description": "Preferences/General; group text"
@ -632,7 +724,7 @@
"description": "Menu text"
},
"remove_batch_downloads_question": {
"message": "Wirklich alle Downloads vom aktuellen Batch entfernen?",
"message": "Wirklich alle Downloads des aktuellen Batch entfernen?",
"description": "Messagebox text"
},
"remove_complete_downloads": {
@ -662,7 +754,7 @@
"description": "Menu text"
},
"remove_domain_complete_downloads_question": {
"message": "Wirklich alle fertigen Downloads mit Domain \"$DOMAIN$\" entfernen?",
"message": "Wirklich alle fertigen Downloads der Domain \"$DOMAIN$\" entfernen?",
"description": "Messagebox text",
"placeholders": {
"domain": {
@ -676,7 +768,7 @@
"description": "Menu text"
},
"remove_domain_downloads_question": {
"message": "Wirklich alle Downloads mit Domain \"$DOMAIN$\" entfernen?",
"message": "Wirklich alle Downloads der Domain \"$DOMAIN$\" entfernen?",
"description": "Messagebox text",
"placeholders": {
"domain": {
@ -720,11 +812,11 @@
}
},
"remove_missing": {
"message": "Fehlende Downloads entfernen",
"message": "Nicht vorhandene Downloads entfernen",
"description": "Menu text"
},
"remove_missing_downloads_question": {
"message": "Wirklich alle fehlenden Downloads entfernen?",
"message": "Wirklich alle nicht vorhandene Downloads entfernen?",
"description": "Messagebox text"
},
"remove_paused_downloads": {
@ -752,15 +844,15 @@
"description": "UI for renaming; currently unused"
},
"renamer_batch": {
"message": "Batch Nummer",
"message": "Batch-Nummer",
"description": "Mask text; see mask button"
},
"renamer_d": {
"message": "Datum hinzugefügt - Tag",
"message": "Datum: hinzugefügt - Tag",
"description": "Mask text; see mask button"
},
"renamer_date": {
"message": "Datum hinzugefügt",
"message": "Datum: hinzugefügt",
"description": "Mask text; see mask button"
},
"renamer_domain": {
@ -772,7 +864,7 @@
"description": "Mask text; see mask button"
},
"renamer_hh": {
"message": "Datum hinzugefügt - Stunde",
"message": "Datum: hinzugefügt - Stunde",
"description": "Mask text; see mask button"
},
"renamer_host": {
@ -780,7 +872,7 @@
"description": "Mask text; see mask button"
},
"renamer_idx": {
"message": "Fortlaufende Nummer im Batch",
"message": "Fortlaufende Nummer des Batch",
"description": "Mask text; see mask button"
},
"renamer_info": {
@ -788,11 +880,11 @@
"description": "Mask text; see mask button; do NOT translate any mentions of \"flat\"!"
},
"renamer_m": {
"message": "Datum hinzugefügt - Monat",
"message": "Datum: hinzugefügt - Monat",
"description": "Mask text; see mask button"
},
"renamer_mm": {
"message": "Datum hinzugefügt - Minute",
"message": "Datum: hinzugefügt - Minute",
"description": "Mask text; see mask button"
},
"renamer_name": {
@ -800,7 +892,7 @@
"description": "Mask text; see mask button"
},
"renamer_num": {
"message": "Alias für \"batch\"",
"message": "Alias für \"Batch\"",
"description": "Mask text; see mask button"
},
"renamer_qstring": {
@ -840,7 +932,7 @@
"description": "Mask text; see mask button"
},
"renamer_ss": {
"message": "Datum hinzugefügt - Sekunde",
"message": "Datum: hinzugefügt - Sekunde",
"description": "Mask text; see mask button"
},
"renamer_subdirs": {
@ -864,7 +956,7 @@
"description": "Mask text; see mask button"
},
"renamer_y": {
"message": "Datum hinzugefügt - Jahr",
"message": "Datum: hinzugefügt - Jahr",
"description": "Mask text; see mask button"
},
"renmask": {
@ -884,17 +976,31 @@
"description": "Messagebox text; pref/General"
},
"reset_layouts": {
"message": "Benutzeroberflächenanpassungen zurücksetzen",
"message": "Änderungen der Benutzeroberfläche zurücksetzen",
"description": "Button text; pref/General"
},
"reset_layouts_done": {
"message": "Alle gemerkten Benutzeroberflächenanpassungen zurückgesetzt! Tabs/Fenster müssen neu geladen werden.",
"message": "Alle gemerkten Anpassungen der Benutzeroberfläche zurückgesetzt! Tabs/Fenster müssen neu geladen werden.",
"description": "Messagebox text; pref/General"
},
"resume_download": {
"message": "Fortsetzen",
"description": "Action for resuming a download"
},
"retrying": {
"message": "neuer Versuch",
"description": "Status text"
},
"retrying_error": {
"message": "Neuversuch - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "Laufend",
"description": "Status text"
@ -920,7 +1026,7 @@
"description": "Menu text; select context"
},
"select_title": {
"message": "DownThemAll! - Downloads auswählen",
"message": "TraitorousDownloading! - Downloads auswählen",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
@ -940,11 +1046,23 @@
"description": "Error message"
},
"set_mask": {
"message": "Umbenennungsmaske setzen",
"message": "Umbenennungsmaske festlegen",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "Neue Umbenennungsmaske festlegen",
"description": "dialog text"
},
"set_referrer": {
"message": "Referrer festlegen",
"description": "menu text"
},
"set_referrer_text": {
"message": "Neuen Referrer festlegen",
"description": "dialog text"
},
"single_batchexamples": {
"message": "Batches sind unterstützt, z.B.:",
"message": "Stapelverarbeitung (Batch) wird unterstützt, z.B.:",
"description": "Header text; single window"
},
"single_header": {
@ -952,7 +1070,7 @@
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - Link hinzufügen",
"message": "TraitorousDownloading! - Link hinzufügen",
"description": "Title of single window"
},
"sizeB": {
@ -1123,6 +1241,14 @@
"message": "Neue Downloads werden nicht gestartet",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "Unterverzeichnis:",
"description": "label text"
},
"subfolder_placeholder": {
"message": "Dateien in diesem Unterverzeichnis im Download-Ordner speichern",
"description": "placeholder text within an input box"
},
"title": {
"message": "Titel",
"description": "Column text; Title label (short)"
@ -1166,5 +1292,9 @@
"useonlyonce": {
"message": "Einmalig",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "Benutzerabbruch",
"description": "Error message"
}
}

View File

@ -191,6 +191,22 @@
"message": "Διαγραφή",
"description": "button text"
},
"deletefiles": {
"message": "Διαγραφή Αρχείων",
"description": "menu action"
},
"deletefiles_button": {
"message": "Διαγραφή",
"description": "button text"
},
"deletefiles_text": {
"message": "Θέλετε σίγουρα να διαγράψετε τα ακόλουθα αρχεία;",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "Διαγραφή Αρχείων",
"description": "messagebox title"
},
"description": {
"message": "Περιγραφή",
"description": "Description (keep it short); e.g. the description column in select"
@ -275,13 +291,21 @@
"message": "Μη έγκυρο URL",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "Οι πλήρεις διαδρομές υποφακέλων δεν υποστηρίζονται από τους φυλλομετρητές",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "Οι τελείες (.) στους υποφακέλους δεν υποστηρίζονται από τους φυλλομετρητές",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "Δεν επιλέχθηκαν αντικείμενα",
"description": "Error Message; select window"
},
"extensionDescription": {
"message": "Ο Διαχειριστής Πολλαπλών Λήψεων για τον φυλλομετρητή σας.",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "Γρήγορο Φιλτράρισμα",
@ -529,6 +553,26 @@
"message": "Προσθήκη νέων λήψεων σε παύση, αντί για άμεση εκκίνησή τους",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "Κουμπί Λήψης Όλων",
"description": "label"
},
"pref_button_type_dta": {
"message": "Επιλογή Λήψης Όλων",
"description": "label"
},
"pref_button_type_manager": {
"message": "Άνοιγμα Διαχειριστή",
"description": "label"
},
"pref_button_type_popup": {
"message": "Αναδυόμενο μενού",
"description": "label"
},
"pref_button_type_turbo": {
"message": "ΜονόΚλικ!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "Ταυτόχρονες λήψεις",
"description": "Preferences/Network"
@ -537,10 +581,6 @@
"message": "Προβολή ειδοποίησης με την ολοκλήρωση της ουράς λήψεων",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "Το κουμπί φυλλομετρητή πρέπει να είναι το ΜονόΚλικ!",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "Απόκρυψη αντικειμένων μενού γενικού περιεχομένου",
"description": "Preferences/General"
@ -549,6 +589,10 @@
"message": "Διαχειριστής",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "Άνοιγμα διαχειριστή σε νέο αναδυόμενο παράθυρο",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "Προβολή επεξηγήσεων στις καρτέλες Διαχειριστή",
"description": "Preferences/General"
@ -573,10 +617,22 @@
"message": "Απομάκρυνση αγνοημένων λήψεων μετά την επανεκκίνηση",
"description": "Preferences/General"
},
"pref_retries": {
"message": "Αριθμός επαναλήψεων λήψεων με προσωρινά σφάλματα",
"description": "pref text"
},
"pref_retry_time": {
"message": "Επανάληψη κάθε (σε λεπτά)",
"description": "pref text"
},
"pref_show_urls": {
"message": "Προβολή URLs αντί για Ονόματα",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "Αναπαραγωγή ήχων",
"description": "checkbox text"
},
"pref_text_links": {
"message": "Δοκιμή εύρεσης συνδέσμων στο κείμενο της ιστοσελίδας (αργό)",
"description": "Preferences/General"
@ -895,6 +951,20 @@
"message": "Συνέχιση",
"description": "Action for resuming a download"
},
"retrying": {
"message": "Επανάληψη",
"description": "Status text"
},
"retrying_error": {
"message": "Επανάληψη - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "Σε λειτουργία",
"description": "Status text"
@ -943,6 +1013,18 @@
"message": "Ορισμός Μάσκας Μετονομασίας",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "Ορισμός μιας νέας μάσκας μετονομασίας",
"description": "dialog text"
},
"set_referrer": {
"message": "Ορισμός Αναφορέα",
"description": "menu text"
},
"set_referrer_text": {
"message": "Ορισμός νέου αναφορέα",
"description": "dialog text"
},
"single_batchexamples": {
"message": "Οι δέσμες υποστηρίζονται, π.χ.:",
"description": "Header text; single window"
@ -1123,6 +1205,14 @@
"message": "Καμία νέα Λήψη δεν θα εκκινηθεί",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "Υποφάκελος:",
"description": "label text"
},
"subfolder_placeholder": {
"message": "Τοποθέτηση αρχείων σε αυτό τον υποφάκελο μέσα στον φάκελο λήψεών σας",
"description": "placeholder text within an input box"
},
"title": {
"message": "Τίτλος",
"description": "Column text; Title label (short)"
@ -1166,5 +1256,9 @@
"useonlyonce": {
"message": "Χρήση μόνο μια φορά",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "Ακύρωση του χρήστη",
"description": "Error message"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -124,7 +124,7 @@
"description": "Table column in manager"
},
"conflict_overwrite": {
"message": "Felülírás",
"message": "Írd felül",
"description": "Option text; prefs/general"
},
"conflict_prompt": {
@ -132,7 +132,7 @@
"description": "Option text; prefs/general"
},
"conflict_rename": {
"message": "Átnevezés",
"message": "Nevezd át",
"description": "Option text; prefs/general"
},
"CRASH": {
@ -191,6 +191,22 @@
"message": "Törlés",
"description": "button text"
},
"deletefiles": {
"message": "Fájlok törlése",
"description": "menu action"
},
"deletefiles_button": {
"message": "Törlés",
"description": "button text"
},
"deletefiles_text": {
"message": "Biztos benne, hogy törli a következő fájlokat?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "Fájlok törlése",
"description": "messagebox title"
},
"description": {
"message": "Leírás",
"description": "Description (keep it short); e.g. the description column in select"
@ -216,27 +232,27 @@
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular": {
"message": "DownThemAll!",
"message": "TraitorousDownloading!",
"description": "Regular dta action; Menu text"
},
"dta_regular_all": {
"message": "DownThemAll! - Összes lap",
"message": "TraitorousDownloading! - Összes lap",
"description": "Menu text"
},
"dta_regular_image": {
"message": "Kép mentése a DownThemAll-al!",
"message": "Kép mentése a TraitorousDownloading-al!",
"description": "Menu text"
},
"dta_regular_link": {
"message": "Hivatkozás mentése DownThemAll-al",
"message": "Hivatkozás mentése TraitorousDownloading-al",
"description": "Menu text"
},
"dta_regular_media": {
"message": "Média mentése DownThemAll-al",
"message": "Média mentése TraitorousDownloading-al",
"description": "Menu text"
},
"dta_regular_selection": {
"message": "Kiválasztás mentése DownThemAll-al",
"message": "Kiválasztás mentése TraitorousDownloading-al",
"description": "Menu text"
},
"dta_turbo": {
@ -275,13 +291,37 @@
"message": "Érvénytelen URL",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "Abszolút útvonalak megadása az almappákhoz nem támogatott a böngészők által",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "Pontok az almappákban nem támogatottak a böngészők által",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "Nincs elem kiválasztva",
"description": "Error Message; select window"
},
"export": {
"message": "Exportálás fájlba",
"description": "menu text"
},
"export_aria2": {
"message": "Exportálás aria2 listaként",
"description": "menu text"
},
"export_metalink": {
"message": "Exportálás Metalink formátumba",
"description": "menu text"
},
"export_text": {
"message": "Exportálás szövegként",
"description": "menu text"
},
"extensionDescription": {
"message": "A tömeges letöltő a böngésződ számára",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "Gyors szűrés",
@ -331,6 +371,10 @@
"message": "Erőltetett indítás",
"description": "Menu text"
},
"import": {
"message": "Importálás fájlból",
"description": "menu text"
},
"information_title": {
"message": "Információ",
"description": "Used in message boxes"
@ -384,7 +428,7 @@
"description": "Menu text"
},
"manager_status_items": {
"message": "Elkészült $COMPLETE$ a $TOTAL$ letöltésből ($SHOWING$ kijelezve), $RUNNING$ folyamatban",
"message": "Elkészült $COMPLETE$ a(z) $TOTAL$ letöltésből ($SHOWING$ kijelezve), $RUNNING$ folyamatban",
"description": "Status bar text; manager",
"placeholders": {
"complete": {
@ -406,7 +450,7 @@
}
},
"manager_title": {
"message": "DownThemAll! kezelő",
"message": "TraitorousDownloading! kezelő",
"description": "Window/tab title"
},
"mask": {
@ -442,7 +486,7 @@
"description": "Action for moving a download up"
},
"nagging_message": {
"message": "Eddig $DOWNLOADS$ letöltést adtál hozzá a DownThemAll!-hoz! Gyakori felhasználóként megfontolhatnád a további fejlesztés támogatását. Köszönöm!",
"message": "Eddig $DOWNLOADS$ letöltést adtál hozzá a TraitorousDownloading!-hoz! Gyakori felhasználóként megfontolhatnád a további fejlesztés támogatását. Köszönöm!",
"description": "Donation nagging message; displayed as a notification bar in manager",
"placeholders": {
"downloads": {
@ -522,25 +566,41 @@
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "DownThemAll! beállításai",
"message": "TraitorousDownloading! beállításai",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "Add hozzá az új letöltéseket szüneteltetve, ahelyett, hogy azonnal megkezdenéd a letöltésüket",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "TraitorousDownloading gomb:",
"description": "label"
},
"pref_button_type_dta": {
"message": "TraitorousDownloading választás",
"description": "label"
},
"pref_button_type_manager": {
"message": "Kezelő megnyitása",
"description": "label"
},
"pref_button_type_popup": {
"message": "Felugró menü",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "Egyidejű letöltések",
"message": "Egyidejű letöltések száma",
"description": "Preferences/Network"
},
"pref_finish_notification": {
"message": "Értesíts, ha a letöltések befejeződtek",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "A böngésző gomb inkább OneClick! legyen",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "Ne mutasd az általános menü elemeit",
"description": "Preferences/General"
@ -549,8 +609,12 @@
"message": "Kezelő",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "Kezelő megnyitása új felugró ablakban",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "Mutass tooltipeket a Kezelő lapokon",
"message": "Mutass tooltipeket a Kezelő lapjain",
"description": "Preferences/General"
},
"pref_netglobal": {
@ -558,7 +622,7 @@
"description": "Preferences/General; group text"
},
"pref_open_manager_on_queue": {
"message": "Nyisd meg a Kezelő lapot a letöltések sorbaállítását követően",
"message": "Nyisd meg a Kezelőt a letöltések sorbaállítását követően",
"description": "Preferences/General"
},
"pref_queueing": {
@ -573,14 +637,42 @@
"message": "Hiányzó letöltések eltávolítása újraindítást követően",
"description": "Preferences/General"
},
"pref_retries": {
"message": "Ideiglenes hibák esetén az újrapróbálkozások száma",
"description": "pref text"
},
"pref_retry_time": {
"message": "Újrapróbálkozás ennyi percenként",
"description": "pref text"
},
"pref_show_urls": {
"message": "Mutass URL-eket nevek helyett",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "Hangok lejátszása",
"description": "checkbox text"
},
"pref_text_links": {
"message": "Keress hivatkozásokat a honlap szövegében (lassabb)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Téma:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Sötét",
"description": "option text"
},
"pref_theme_default": {
"message": "Rendszer/Böngésző",
"description": "option text"
},
"pref_theme_light": {
"message": "Világos",
"description": "option text"
},
"pref_ui": {
"message": "Felhasználói felület",
"description": "Preferences/General; group text"
@ -895,6 +987,20 @@
"message": "Folytatás",
"description": "Action for resuming a download"
},
"retrying": {
"message": "Újrapróbálkozás",
"description": "Status text"
},
"retrying_error": {
"message": "Újrapróbálkozás - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "Folyamatban",
"description": "Status text"
@ -920,7 +1026,7 @@
"description": "Menu text; select context"
},
"select_title": {
"message": "DownThemAll! - Válaszd ki a letöltéseidet",
"message": "TraitorousDownloading! - Válaszd ki a letöltéseidet",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
@ -943,16 +1049,28 @@
"message": "Átnevezési maszk beállítása",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "Új átnevezési maszk beállítása",
"description": "dialog text"
},
"set_referrer": {
"message": "Referrer beállítása",
"description": "menu text"
},
"set_referrer_text": {
"message": "Új referrer beállítása",
"description": "dialog text"
},
"single_batchexamples": {
"message": "Kötegek támogatva vannak, pl.:",
"message": "Kötegelés támogatva van, pl.:",
"description": "Header text; single window"
},
"single_header": {
"message": "Írj be egy letöltési URL-t (hivatkozást) és egyéb beállításokat",
"message": "Írjon be egy letöltési URL-t (hivatkozást) és adjon meg egyéb beállításokat",
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - Adj hozzá egy hivatkozást",
"message": "TraitorousDownloading! - Adj hozzá egy hivatkozást",
"description": "Title of single window"
},
"sizeB": {
@ -1123,6 +1241,14 @@
"message": "Nem lesznek új letöltések elindítva",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "Almappa:",
"description": "label text"
},
"subfolder_placeholder": {
"message": "Fájlok elhelyezése ebbe az almappába a letöltések mappáján belül",
"description": "placeholder text within an input box"
},
"title": {
"message": "Cím",
"description": "Column text; Title label (short)"
@ -1166,5 +1292,9 @@
"useonlyonce": {
"message": "Egyszer használd",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "Felhasználói megszakítás",
"description": "Error message"
}
}

View File

@ -7,18 +7,6 @@
"message": "id",
"description": "Language code the locale will use, e.g. de or en-GB or pt-BR"
},
"renamer_tags": {
"message": "Penanda Mask Penamaan",
"description": "Mask text; see mask button"
},
"renmask": {
"message": "Mask penamaan",
"description": "Renaming mask (long)"
},
"set_mask": {
"message": "Set Mask Penamaan",
"description": "Menu text; select window"
},
"addpaused": {
"message": "Tambahkan dalam kondisi terpause",
"description": "Action: Add paused"
@ -36,7 +24,7 @@
"description": "Checkbox label"
},
"add_paused_question": {
"message": "Apa Anda ingin mengingat keputusan ini dan mulai sekarang tambahkan unduhan dalam kondisi terpause?",
"message": "Ingat keputusan ini dan tambahkan unduhan dalam kondisi terpause mulai sekarang?",
"description": "Messagebox text"
},
"add_paused_title": {
@ -187,6 +175,22 @@
"message": "Hapus",
"description": "button text"
},
"deletefiles": {
"message": "Hapus Berkas",
"description": "menu action"
},
"deletefiles_button": {
"message": "Hapus",
"description": "button text"
},
"deletefiles_text": {
"message": "Hapus berkas-berkas berikut ini?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "Hapus Berkas",
"description": "messagebox title"
},
"description": {
"message": "Keterangan",
"description": "Description (keep it short); e.g. the description column in select"
@ -212,23 +216,23 @@
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular_all": {
"message": "DownThemAll - Semua Tab",
"message": "TraitorousDownloading - Semua Tab",
"description": "Menu text"
},
"dta_regular_image": {
"message": "Simpan Gambar menggunakan DownThemAll!",
"message": "Simpan Gambar menggunakan TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_link": {
"message": "Simpan Tautan menggunakan DownThemAll!",
"message": "Simpan Tautan menggunakan TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_media": {
"message": "Simpan Media menggunakan DownThemAll!",
"message": "Simpan Media menggunakan TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_selection": {
"message": "Simpan Pilihan menggunakan DownThemAll!",
"message": "Simpan Pilihan menggunakan TraitorousDownloading!",
"description": "Menu text"
},
"dta_turbo_all": {
@ -263,13 +267,37 @@
"message": "URL Tidak Benar",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "Path absolut untuk subfolder tidak didukung oleh peramban",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "Titik (.) di subfolder tidak didukung oleh peramban",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "Tidak ada item terpilih",
"description": "Error Message; select window"
},
"export": {
"message": "Ekspor Ke Berkas",
"description": "menu text"
},
"export_aria2": {
"message": "Ekspor Sebagai Daftar aria2",
"description": "menu text"
},
"export_metalink": {
"message": "Ekspor Sebagai Metalink",
"description": "menu text"
},
"export_text": {
"message": "Ekspor Sebagai Teks",
"description": "menu text"
},
"extensionDescription": {
"message": "Pengunduh Masal untuk browser anda",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "Penyaringan Cepat",
@ -319,6 +347,10 @@
"message": "Paksa Mulai",
"description": "Menu text"
},
"import": {
"message": "Impor Dari Berkas",
"description": "menu text"
},
"information_title": {
"message": "Informasi",
"description": "Used in message boxes"
@ -366,7 +398,7 @@
}
},
"manager_title": {
"message": "Pengelola DownThemAll!",
"message": "Pengelola TraitorousDownloading!",
"description": "Window/tab title"
},
"mask_default": {
@ -394,7 +426,7 @@
"description": "Action for moving a download up"
},
"nagging_message": {
"message": "Sejauh ini Anda telah menambahkan $DOWNLOADS$ unduhan menggunakan DownThemAll! Sebagai penggunan setia, silakan berdonasi untuk mendukung pengembangan lebih lanjut. Terima Kasih!",
"message": "Sejauh ini Anda telah menambahkan $DOWNLOADS$ unduhan menggunakan TraitorousDownloading! Sebagai penggunan setia, silakan berdonasi untuk mendukung pengembangan lebih lanjut. Terima Kasih!",
"description": "Donation nagging message; displayed as a notification bar in manager",
"placeholders": {
"downloads": {
@ -466,13 +498,29 @@
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "Preferensi DownThemAll!",
"message": "Preferensi TraitorousDownloading!",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "Tambahkan unduhan terpause, alih-alih langsung memulai mengunduh",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "Tombol TraitorousDownloading!:",
"description": "label"
},
"pref_button_type_dta": {
"message": "Pilihan TraitorousDownloading!",
"description": "label"
},
"pref_button_type_manager": {
"message": "Buka Pengelola",
"description": "label"
},
"pref_button_type_popup": {
"message": "Menu popup",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "Unduhan Bersamaan",
"description": "Preferences/Network"
@ -481,10 +529,6 @@
"message": "Tampilkan pemberitahuan ketika antrian unduhan selesai",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "Tombol peramban sebaiknya OneClick!",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "Jangan perlihatkan item menu general context",
"description": "Preferences/General"
@ -493,6 +537,10 @@
"message": "Pengelola",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "Buka pengelola di jendela popup baru",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "Tampilkan tooltip di tab Pengelola",
"description": "Preferences/General"
@ -517,10 +565,22 @@
"message": "Setelah restart, hapus unduhan yang tidak ada",
"description": "Preferences/General"
},
"pref_retries": {
"message": "Jumlah percobaan ulang ketika gagal mengunduh",
"description": "pref text"
},
"pref_retry_time": {
"message": "Coba setiap (dalam menit)",
"description": "pref text"
},
"pref_show_urls": {
"message": "Tampilan URL alih-alih Nama",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "Bunyikan suara",
"description": "checkbox text"
},
"pref_text_links": {
"message": "Coba cari tautan di teks di situs web (lambat)",
"description": "Preferences/General"
@ -779,6 +839,10 @@
"message": "Tanggal Ditambahkan - Detik",
"description": "Mask text; see mask button"
},
"renamer_tags": {
"message": "Penanda Mask Penamaan",
"description": "Mask text; see mask button"
},
"renamer_text": {
"message": "Teks Deskripsi",
"description": "Mask text; see mask button"
@ -795,6 +859,10 @@
"message": "Tanggal Ditambahkan - Tahun",
"description": "Mask text; see mask button"
},
"renmask": {
"message": "Mask penamaan",
"description": "Renaming mask (long)"
},
"reset": {
"message": "Atur Ulang",
"description": "Button text; pref window"
@ -819,6 +887,20 @@
"message": "Lanjutkan",
"description": "Action for resuming a download"
},
"retrying": {
"message": "Mengulang",
"description": "Status text"
},
"retrying_error": {
"message": "Mengulang - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "Berjalan",
"description": "Status text"
@ -844,7 +926,7 @@
"description": "Menu text; select context"
},
"select_title": {
"message": "DownThemAll! - Pilih Unduhan Anda",
"message": "TraitorousDownloading! - Pilih Unduhan Anda",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
@ -863,6 +945,22 @@
"message": "Tidak Diizinkan",
"description": "Error message"
},
"set_mask": {
"message": "Set Mask Penamaan",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "Tentukan mask penamaan baru",
"description": "dialog text"
},
"set_referrer": {
"message": "Tentukan Referrer",
"description": "menu text"
},
"set_referrer_text": {
"message": "Tentukan referrer baru",
"description": "dialog text"
},
"single_batchexamples": {
"message": "Batch bisa digunakan, misalnya:",
"description": "Header text; single window"
@ -872,7 +970,7 @@
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - Tambah tautan",
"message": "TraitorousDownloading! - Tambah tautan",
"description": "Title of single window"
},
"sizes_huge": {
@ -983,6 +1081,10 @@
"message": "Tidak ada Unduhan baru yang akan dimulai",
"description": "Status bar tooltip; manager network icon"
},
"subfolder_placeholder": {
"message": "Simpan berkas di subfolder berikut di dalam direktori unduhan Anda",
"description": "placeholder text within an input box"
},
"title": {
"message": "Judul",
"description": "Column text; Title label (short)"
@ -1026,5 +1128,9 @@
"useonlyonce": {
"message": "Gunakan Sekali",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "Dibatalkan Pengguna",
"description": "Error message"
}
}

View File

@ -7,26 +7,6 @@
"message": "it-IT",
"description": "Language code the locale will use, e.g. de or en-GB or pt-BR"
},
"renamer_batch": {
"message": "Numero gruppo",
"description": "Mask text; see mask button"
},
"renamer_idx": {
"message": "Numero elemento nel gruppo di download",
"description": "Mask text; see mask button"
},
"renamer_num": {
"message": "Alias per *batch*",
"description": "Mask text; see mask button"
},
"renamer_refqstring": {
"message": "Stringa Query Referrer",
"description": "Mask text; see mask button"
},
"single_batchexamples": {
"message": "I download raggruppati sono supportati, ad esempio:",
"description": "Header text; single window"
},
"addpaused": {
"message": "Aggiungi in pausa",
"description": "Action: Add paused"
@ -36,7 +16,7 @@
"description": "Action for adding a download"
},
"add_new": {
"message": "Aggiungi nuovo",
"message": "Aggiungi Nuovo",
"description": "Button text (adding filters, limits and such)"
},
"add_paused_once": {
@ -44,11 +24,11 @@
"description": "Checkbox label"
},
"add_paused_question": {
"message": "Vuoi ricordare questa decisione e aggiungere sempre",
"message": "Vuoi ricordare questa scelta e aggiungere sempre nuovi download in pausa da ora in poi?",
"description": "Messagebox text"
},
"add_paused_title": {
"message": "Vuoi sempre aggiungere in pausa?",
"message": "Vuoi aggiungere sempre in pausa?",
"description": "Title for the add-paused dialog"
},
"ask_again_later": {
@ -56,11 +36,11 @@
"description": "Button text"
},
"batch_batch": {
"message": "Download di gruppo",
"message": "Download Multiplo",
"description": "Button text"
},
"batch_desc": {
"message": "L'indirizzo attuale sembra contenere istruzioni per un download di massa.",
"message": "L'URL corrente sembra contenere istruzioni per un download multiplo.",
"description": ""
},
"batch_items": {
@ -72,15 +52,15 @@
"description": "Messagebox info text for batch confirmations"
},
"batch_question": {
"message": "Vuoi aggiungerlo come download di massa o singolo?",
"message": "Vuoi aggiungere questo come download multiplo o singolo?",
"description": "Messagebox info text for batch confirmations"
},
"batch_single": {
"message": "Download singolo",
"message": "Download Singolo",
"description": "Button text for batch confirmation"
},
"batch_title": {
"message": "Download di massa",
"message": "Download Multiplo",
"description": "Messagebox title for batch confirmations"
},
"cancel": {
@ -100,7 +80,7 @@
"description": "Error message when an input field is empty but has to have a value"
},
"change_later_reminder": {
"message": "Puoi cambiare la tua decisione nelle Impostazioni",
"message": "Puoi modificare in seguito questa scelta nelle Preferenze",
"description": "Checkbox label text for decision confirmations"
},
"check_selected_items": {
@ -116,7 +96,7 @@
"description": "Table column in manager"
},
"colETA": {
"message": "ETA",
"message": "Tempo rimanente",
"description": "Table column in manager"
},
"colNameURL": {
@ -164,7 +144,7 @@
"description": "Button text; Create filter dialog; prefs/filters"
},
"custom_filename": {
"message": "Nome personalizzato",
"message": "Nome file personalizzato",
"description": "Label text; single window"
},
"deffilter_all": {
@ -180,7 +160,7 @@
"description": "Filter label for the Audio filter"
},
"deffilter_bin": {
"message": "Software (exe, msi, …)",
"message": "Programmi (exe, msi, …)",
"description": "Filter label for the Software filter"
},
"deffilter_doc": {
@ -211,12 +191,28 @@
"message": "Elimina",
"description": "button text"
},
"deletefiles": {
"message": "Elimina File",
"description": "menu action"
},
"deletefiles_button": {
"message": "Elimina",
"description": "button text"
},
"deletefiles_text": {
"message": "Sei sicuro di voler eliminare i seguenti file?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "Elimina File",
"description": "messagebox title"
},
"description": {
"message": "Descrizione",
"description": "Description (keep it short); e.g. the description column in select"
},
"disable_other_filters": {
"message": "Disabilita gli altri",
"message": "Disabilita gli altri filtri",
"description": "Checkbox label. Keep it short"
},
"donate": {
@ -236,27 +232,27 @@
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular": {
"message": "DownThemAll!",
"message": "TraitorousDownloading!",
"description": "Regular dta action; Menu text"
},
"dta_regular_all": {
"message": "DownThemAll! - Tutte le schede",
"message": "TraitorousDownloading! - Tutte le schede",
"description": "Menu text"
},
"dta_regular_image": {
"message": "Salva immagine con DownThemAll!",
"message": "Salva immagine con TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_link": {
"message": "Salva link con DownThemAll!",
"message": "Salva link con TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_media": {
"message": "Salva media con DownThemAll!",
"message": "Salva media con TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_selection": {
"message": "Salva selezione con DownThemAll!",
"message": "Salva selezione con TraitorousDownloading!",
"description": "Menu text"
},
"dta_turbo": {
@ -284,31 +280,55 @@
"description": "Menu text"
},
"error_invalidMask": {
"message": "Maschera di rinominazione non valida",
"message": "Maschera di rinomina non valida",
"description": "Error message; single/select window"
},
"error_invalidReferrer": {
"message": "Referrer invalido",
"message": "Referrer non valido",
"description": "Error message; single window"
},
"error_invalidURL": {
"message": "URL non valido",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "I percorsi assoluti per le sottocartelle non sono supportati dal browser",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "I punti (.) nelle sottocartelle non sono supportati dal browser",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "Nessun elemento selezionato",
"description": "Error Message; select window"
},
"export": {
"message": "Esporta in un File",
"description": "menu text"
},
"export_aria2": {
"message": "Esporta Lista come aria2",
"description": "menu text"
},
"export_metalink": {
"message": "Esporta come Metalink",
"description": "menu text"
},
"export_text": {
"message": "Esporta come Testo",
"description": "menu text"
},
"extensionDescription": {
"message": "Il download manager di massa per il tuo browser",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "Filtraggio Rapido",
"message": "Filtro Rapido",
"description": "Label for Fast Filtering input"
},
"fastfilter_placeholder": {
"message": "Espressione con asterischi (*) oppure regolare",
"message": "Espressione con parole chiave o espressione regolare",
"description": "Placeholder for fastfilter inputs"
},
"FILE_FAILED": {
@ -320,11 +340,11 @@
"description": "Error message when no filter types are selected for a filter in the preferences UI"
},
"filter_create_title": {
"message": "Crea nuovo filtro",
"message": "Crea nuovo Filtro",
"description": "Message box title"
},
"filter_expression": {
"message": "Valore Filtro",
"message": "Espressione Filtro",
"description": "Message box label"
},
"filter_label": {
@ -351,6 +371,10 @@
"message": "Forza avvio",
"description": "Menu text"
},
"import": {
"message": "Importa da File",
"description": "menu text"
},
"information_title": {
"message": "Informazione",
"description": "Used in message boxes"
@ -364,7 +388,7 @@
"description": "Menu text"
},
"key_alt": {
"message": "Atl",
"message": "Alt",
"description": "Short for Alt-Key"
},
"key_ctrl": {
@ -384,11 +408,11 @@
"description": "Short for home key"
},
"key_pagedown": {
"message": "PagGIU",
"message": "PagGiu",
"description": "Short for pagedown-key"
},
"key_pageup": {
"message": "PagSU",
"message": "PagSu",
"description": "Short for PageUp-key"
},
"limited_to": {
@ -426,7 +450,7 @@
}
},
"manager_title": {
"message": "DownThemAll! Manager",
"message": "TraitorousDownloading! Manager",
"description": "Window/tab title"
},
"mask": {
@ -446,7 +470,7 @@
"description": "Status text in manager"
},
"move_bottom": {
"message": "In basso",
"message": "In fondo",
"description": "Action for moving a download to the bottom"
},
"move_down": {
@ -454,7 +478,7 @@
"description": "Action for moving a download down"
},
"move_top": {
"message": "In alto",
"message": "In cima",
"description": "Action for moving a download to the top"
},
"move_up": {
@ -462,7 +486,7 @@
"description": "Action for moving a download up"
},
"nagging_message": {
"message": "Hai aggiunto $DOWNLOADS$ download a DownThemAll! Come utente abituale, potresti considerare una donazione per supportare lo sviluppo. Grazie!",
"message": "Hai aggiunto $DOWNLOADS$ download a TraitorousDownloading! Come utente abituale potresti considerare una donazione per supportare lo sviluppo. Grazie!",
"description": "Donation nagging message; displayed as a notification bar in manager",
"placeholders": {
"downloads": {
@ -538,17 +562,37 @@
"description": "Preferences/General; group text"
},
"prefs_short": {
"message": "Impostazioni",
"message": "Preferenze",
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "Impostazioni di DownThemAll!",
"message": "Preferenze di TraitorousDownloading!",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "Aggiungi nuovi download in pausa, invece di avviarli subito",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "Pulsante TraitorousDownloading!:",
"description": "label"
},
"pref_button_type_dta": {
"message": "Selezione TraitorousDownloading!",
"description": "label"
},
"pref_button_type_manager": {
"message": "Apri Manager",
"description": "label"
},
"pref_button_type_popup": {
"message": "Menu Popup",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "Download contemporanei",
"description": "Preferences/Network"
@ -557,18 +601,18 @@
"message": "Mostra una notifica quando la coda finisce di scaricare",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "Il pulsante del browser diventa OneClick!",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "Non mostrare menù contestuali ovunque",
"message": "Non mostrare elementi nel menù contestuale",
"description": "Preferences/General"
},
"pref_manager": {
"message": "Manager",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "Apri Manager in una nuova finestra popup",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "Mostra suggerimenti nelle schede del Manager",
"description": "Preferences/General"
@ -578,11 +622,11 @@
"description": "Preferences/General; group text"
},
"pref_open_manager_on_queue": {
"message": "Apri la scheda del Manager dopo aver aggiunto nuovi download",
"message": "Apri la scheda Manager dopo aver aggiunto nuovi download",
"description": "Preferences/General"
},
"pref_queueing": {
"message": "Coda dei Download",
"message": "Coda di Download",
"description": "Preferences/General; group text"
},
"pref_queue_notification": {
@ -593,16 +637,28 @@
"message": "Rimuovi i download mancanti dopo un riavvio",
"description": "Preferences/General"
},
"pref_retries": {
"message": "Numero di nuovi tentativi di download per errori temporanei",
"description": "pref text"
},
"pref_retry_time": {
"message": "Riprova ogni (in minuti)",
"description": "pref text"
},
"pref_show_urls": {
"message": "Mostra indirizzi invece dei nomi",
"message": "Mostra URL invece dei Nomi",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "Esegui suoni",
"description": "checkbox text"
},
"pref_text_links": {
"message": "Cerca link nel testo del sito (più lento)",
"message": "Cerca link nel testo della pagina (più lento)",
"description": "Preferences/General"
},
"pref_ui": {
"message": "Interfaccia utente",
"message": "Interfaccia Utente",
"description": "Preferences/General; group text"
},
"queued": {
@ -610,11 +666,11 @@
"description": "Status text"
},
"queued_download": {
"message": "1 download è stato messo in coda!",
"message": "1 download in coda!",
"description": "Notification text; single download"
},
"queued_downloads": {
"message": "$COUNT$ download sono stati messi in coda!",
"message": "$COUNT$ download in coda!",
"description": "Notification text; multiple downloads",
"placeholders": {
"count": {
@ -632,11 +688,11 @@
"description": "Label for \"Referrer\""
},
"remember": {
"message": "Ricorda questa decisione",
"message": "Ricorda questa scelta",
"description": "Checkbox text for confirmation, e.g. when removing a download in manager"
},
"remove_all_complete_downloads": {
"message": "Rimuovi completati",
"message": "Rimuovi tutti i completati",
"description": "Menu text"
},
"remove_all_downloads": {
@ -648,11 +704,11 @@
"description": "Messagebox text"
},
"remove_batch_downloads": {
"message": "Rimuovi gruppo attuale",
"message": "Rimuovi download multiplo corrente",
"description": "Menu text"
},
"remove_batch_downloads_question": {
"message": "Vuoi rimuovere tutti i download dello stesso gruppo dal download attualmente selezionato?",
"message": "Vuoi rimuovere tutti i download dello stesso gruppo del download (multiplo) attualmente selezionato?",
"description": "Messagebox text"
},
"remove_complete_downloads": {
@ -674,7 +730,7 @@
}
},
"remove_complete_selection_downloads_question": {
"message": "Vuoi rimuovere tutti i download completati nella selezione attuale?",
"message": "Vuoi rimuovere tutti i download completati nella selezione corrente?",
"description": "Messagebox text"
},
"remove_domain_complete_downloads": {
@ -692,7 +748,7 @@
}
},
"remove_domain_downloads": {
"message": "Rimuovi dominio attuale",
"message": "Rimuovi download dal dominio attuale",
"description": "Menu text"
},
"remove_domain_downloads_question": {
@ -710,7 +766,7 @@
"description": "Action for removing a download, no matter what state"
},
"remove_downloads": {
"message": "Rimuovi download",
"message": "Rimuovi i download",
"description": "Menu text"
},
"remove_downloads_title": {
@ -722,7 +778,7 @@
"description": "Messagebox text"
},
"remove_failed_downloads": {
"message": "Rimuovi falliti",
"message": "Rimuovi i download falliti",
"description": "Menu text"
},
"remove_failed_downloads_question": {
@ -740,7 +796,7 @@
}
},
"remove_missing": {
"message": "Cancella download mancanti",
"message": "Cancella i download mancanti",
"description": "Menu text"
},
"remove_missing_downloads_question": {
@ -748,7 +804,7 @@
"description": "Messagebox text"
},
"remove_paused_downloads": {
"message": "Rimuovi in pausa",
"message": "Rimuovi i download in pausa",
"description": "Menu text"
},
"remove_paused_downloads_question": {
@ -756,7 +812,7 @@
"description": "Messagebox text"
},
"remove_selected_complete_downloads": {
"message": "Rimuovi i completati nella selezione",
"message": "Rimuovi i download completati nella selezione",
"description": "Menu text"
},
"remove_selected_complete_downloads_question": {
@ -764,13 +820,17 @@
"description": "Messagebox text"
},
"remove_selected_downloads": {
"message": "Rimuovi selezionati",
"message": "Rimuovi i download selezionati",
"description": "Menu text"
},
"rename": {
"message": "Rinomina",
"description": "UI for renaming; currently unused"
},
"renamer_batch": {
"message": "Numero gruppo",
"description": "Mask text; see mask button"
},
"renamer_d": {
"message": "Data aggiunta - Giorno",
"description": "Mask text; see mask button"
@ -792,11 +852,15 @@
"description": "Mask text; see mask button"
},
"renamer_host": {
"message": "Hostname",
"message": "Nome Host",
"description": "Mask text; see mask button"
},
"renamer_idx": {
"message": "Numero elemento nel gruppo di download",
"description": "Mask text; see mask button"
},
"renamer_info": {
"message": "Aggiungendo 'flat', come *flatsubdirs* sostituirà tutti gli slash nel valore, quindi non verranno create cartelle",
"message": "Aggiungendo 'flat', come ad es. *flatsubdirs* sostituirà tutti gli slash nel valore, quindi non verranno create cartelle",
"description": "Mask text; see mask button; do NOT translate any mentions of \"flat\"!"
},
"renamer_m": {
@ -811,6 +875,10 @@
"message": "Nome file",
"description": "Mask text; see mask button"
},
"renamer_num": {
"message": "Alias per *batch*",
"description": "Mask text; see mask button"
},
"renamer_qstring": {
"message": "Stringa Querry",
"description": "Mask text; see mask button"
@ -835,6 +903,10 @@
"message": "Nome file Refferer",
"description": "Mask text; see mask button"
},
"renamer_refqstring": {
"message": "Stringa Query Referrer",
"description": "Mask text; see mask button"
},
"renamer_refsubdirs": {
"message": "Percorso Referrer",
"description": "Mask text; see mask button"
@ -844,7 +916,7 @@
"description": "Mask text; see mask button"
},
"renamer_ss": {
"message": "Data aggiunta - Seconda",
"message": "Data aggiunta - Secondi",
"description": "Mask text; see mask button"
},
"renamer_subdirs": {
@ -852,15 +924,15 @@
"description": "Mask text; see mask button"
},
"renamer_tags": {
"message": "Tag per Maschera di rinominazione",
"message": "Tag maschera di rinomina",
"description": "Mask text; see mask button"
},
"renamer_text": {
"message": "Descrizione",
"message": "Testo descrizione",
"description": "Mask text; see mask button"
},
"renamer_title": {
"message": "Titolo",
"message": "Testo titolo",
"description": "Mask text; see mask button"
},
"renamer_url": {
@ -872,7 +944,7 @@
"description": "Mask text; see mask button"
},
"renmask": {
"message": "Maschera di Rinominazione",
"message": "Maschera di rinomina",
"description": "Renaming mask (long)"
},
"reset": {
@ -880,25 +952,39 @@
"description": "Button text; pref window"
},
"reset_confirmations": {
"message": "Ripristina conferme memorizzate",
"message": "Ripristina scelte salvate",
"description": "Button text; pref/General"
},
"reset_confirmations_done": {
"message": "Tutte le conferme precedentemente memorizzate sono state resettate!",
"message": "Tutte le scelte precedentemente salvate sono state ripristinate!",
"description": "Messagebox text; pref/General"
},
"reset_layouts": {
"message": "Ripristina personalizzazioni grafiche",
"message": "Ripristina personalizzazioni interfaccia utente",
"description": "Button text; pref/General"
},
"reset_layouts_done": {
"message": "Tutte le personalizzazioni grafiche sono state ripristinate! Ricarica le finestre/schede per applicarle.",
"message": "Tutte le personalizzazioni grafiche salvate sono state ripristinate! Potresti dover ricaricare le finestre/schede.",
"description": "Messagebox text; pref/General"
},
"resume_download": {
"message": "Ripristina",
"message": "Riprendi",
"description": "Action for resuming a download"
},
"retrying": {
"message": "Nuovo tentativo",
"description": "Status text"
},
"retrying_error": {
"message": "Nuovo tentativo - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "In esecuzione",
"description": "Status text"
@ -924,7 +1010,7 @@
"description": "Menu text; select context"
},
"select_title": {
"message": "DownThemAll! - Seleziona i tuoi download",
"message": "TraitorousDownloading! - Seleziona i tuoi download",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
@ -944,15 +1030,31 @@
"description": "Error message"
},
"set_mask": {
"message": "Definisci Maschera di Rinominazione",
"message": "Imposta maschera di rinomina",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "Imposta nuova maschera di rinomina",
"description": "dialog text"
},
"set_referrer": {
"message": "Imposta Referrer",
"description": "menu text"
},
"set_referrer_text": {
"message": "Imposta nuovo Referrer",
"description": "dialog text"
},
"single_batchexamples": {
"message": "I download multipli sono supportati, ad es.:",
"description": "Header text; single window"
},
"single_header": {
"message": "Inserisci un indirizzo di download (link) e altre opzioni",
"message": "Inserisci un URL di download (link) e altre opzioni",
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - Aggiungi un link",
"message": "TraitorousDownloading! - Aggiungi un link",
"description": "Title of single window"
},
"sizeB": {
@ -1006,7 +1108,7 @@
}
},
"sizes_huge": {
"message": "Gigante (> $HIGH$)",
"message": "Molto grande (> $HIGH$)",
"description": "Menu text; manager size column dropdown",
"placeholders": {
"high": {
@ -1068,7 +1170,7 @@
}
},
"size_progress": {
"message": "$WRITTEN$ of $TOTAL$",
"message": "$WRITTEN$ di $TOTAL$",
"description": "Status text; manager size column",
"placeholders": {
"total": {
@ -1120,15 +1222,23 @@
"description": "Status bar tooltip; manager network icon"
},
"statusNetwork_inactive_title": {
"message": "I nuovi download non verranno avviati",
"message": "Nessun nuovo download verrà avviato",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "Sottocartella:",
"description": "label text"
},
"subfolder_placeholder": {
"message": "Inserisci file in questa sottocartella della tua cartella di download",
"description": "placeholder text within an input box"
},
"title": {
"message": "Titolo",
"description": "Column text; Title label (short)"
},
"toggle_selected_items": {
"message": "Inverti selezione",
"message": "Inverti contrassegno per gli elementi selezionati",
"description": "Menu text; select"
},
"tooltip_date": {
@ -1156,7 +1266,7 @@
"description": "Tooltip text; manager/downloads"
},
"uncheck_selected_items": {
"message": "Non contrassegnare elementi selezionati",
"message": "Togli contrassegno agli elementi selezionati",
"description": "Menu text; select"
},
"unlimited": {
@ -1166,5 +1276,9 @@
"useonlyonce": {
"message": "Usa una volta",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "Annullato dall'utente",
"description": "Error message"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -191,6 +191,22 @@
"message": "삭제",
"description": "button text"
},
"deletefiles": {
"message": "파일 삭제",
"description": "menu action"
},
"deletefiles_button": {
"message": "삭제",
"description": "button text"
},
"deletefiles_text": {
"message": "정말 다음 파일(들)을 삭제하시겠습니까?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "파일 삭제",
"description": "messagebox title"
},
"description": {
"message": "설명",
"description": "Description (keep it short); e.g. the description column in select"
@ -216,27 +232,27 @@
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular": {
"message": "DownThemAll!",
"message": "TraitorousDownloading!",
"description": "Regular dta action; Menu text"
},
"dta_regular_all": {
"message": "DownThemAll! - 모든 탭",
"message": "TraitorousDownloading! - 모든 탭",
"description": "Menu text"
},
"dta_regular_image": {
"message": "이미지를 DownThemAll!로 저장",
"message": "이미지를 TraitorousDownloading!로 저장",
"description": "Menu text"
},
"dta_regular_link": {
"message": "링크를 DownThemAll!로 저장",
"message": "링크를 TraitorousDownloading!로 저장",
"description": "Menu text"
},
"dta_regular_media": {
"message": "미디어를 DownThemAll!로 저장",
"message": "미디어를 TraitorousDownloading!로 저장",
"description": "Menu text"
},
"dta_regular_selection": {
"message": "선택영역을 DownThemAll!로 저장",
"message": "선택영역을 TraitorousDownloading!로 저장",
"description": "Menu text"
},
"dta_turbo": {
@ -275,13 +291,37 @@
"message": "잘못된 URL",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "하위 폴더의 절대 경로는 브라우저에서 지원되지 않습니다",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "하위 폴더의 점(.)은 브라우저에서 지원되지 않습니다",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "선택된 항목 없음",
"description": "Error Message; select window"
},
"export": {
"message": "파일로 내보내기",
"description": "menu text"
},
"export_aria2": {
"message": "aria2 리스트로 내보내기",
"description": "menu text"
},
"export_metalink": {
"message": "메타링크로 내보내기",
"description": "menu text"
},
"export_text": {
"message": "텍스트로 내보내기",
"description": "menu text"
},
"extensionDescription": {
"message": "브라우저용 대량 다운로더",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "빠른 필터링",
@ -331,6 +371,10 @@
"message": "강제 시작",
"description": "Menu text"
},
"import": {
"message": "파일에서 가져오기",
"description": "menu text"
},
"information_title": {
"message": "정보",
"description": "Used in message boxes"
@ -406,7 +450,7 @@
}
},
"manager_title": {
"message": "DownThemAll! 관리자",
"message": "TraitorousDownloading! 관리자",
"description": "Window/tab title"
},
"mask": {
@ -442,7 +486,7 @@
"description": "Action for moving a download up"
},
"nagging_message": {
"message": "지금까지 DownThemAll!로 $DOWNLOADS$ 다운로드를 추가하셨습니다. 일반 사용자로서 추가 개발을 지원하기 위해 기부를 고려할 수 있습니다. 감사합니다!",
"message": "지금까지 TraitorousDownloading!로 $DOWNLOADS$ 다운로드를 추가하셨습니다. 일반 사용자로서 추가 개발을 지원하기 위해 기부를 고려할 수 있습니다. 감사합니다!",
"description": "Donation nagging message; displayed as a notification bar in manager",
"placeholders": {
"downloads": {
@ -522,13 +566,33 @@
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "DownThemAll! 설정",
"message": "TraitorousDownloading! 설정",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "새 다운로드를 즉시 시작하는 대신 일시중지로 추가",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "TraitorousDownloading! 버튼:",
"description": "label"
},
"pref_button_type_dta": {
"message": "TraitorousDownloading! 선택",
"description": "label"
},
"pref_button_type_manager": {
"message": "관리자 열기",
"description": "label"
},
"pref_button_type_popup": {
"message": "팝업 메뉴",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "동시 다운로드",
"description": "Preferences/Network"
@ -537,10 +601,6 @@
"message": "다운로드 대기열이 끝나면 알림 표시",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "브라우저 버튼을 OneClick!으로 작동",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "일반 컨텍스트 메뉴 항목 표시 안 함",
"description": "Preferences/General"
@ -549,6 +609,10 @@
"message": "관리자",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "새 팝업 창에 관리자 열기",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "관리자 탭에서 툴팁 표시",
"description": "Preferences/General"
@ -573,10 +637,22 @@
"message": "다시 시작한 후 누락된 다운로드 제거",
"description": "Preferences/General"
},
"pref_retries": {
"message": "일시적인 오류에 대한 다운로드 재시도 횟수",
"description": "pref text"
},
"pref_retry_time": {
"message": "재시도 간격 (분)",
"description": "pref text"
},
"pref_show_urls": {
"message": "이름 대신 URL 표시",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "소리 재생",
"description": "checkbox text"
},
"pref_text_links": {
"message": "웹 사이트 텍스트에서 링크를 찾도록 시도 (느림)",
"description": "Preferences/General"
@ -895,6 +971,20 @@
"message": "계속",
"description": "Action for resuming a download"
},
"retrying": {
"message": "재시도중",
"description": "Status text"
},
"retrying_error": {
"message": "재시도중 - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "실행중",
"description": "Status text"
@ -920,7 +1010,7 @@
"description": "Menu text; select context"
},
"select_title": {
"message": "DownThemAll! - 다운로드 선택",
"message": "TraitorousDownloading! - 다운로드 선택",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
@ -943,6 +1033,18 @@
"message": "이름 바꾸기 마스크 설정",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "새 이름 바꾸기 마스크 설정",
"description": "dialog text"
},
"set_referrer": {
"message": "참조 페이지 설정",
"description": "menu text"
},
"set_referrer_text": {
"message": "새 참조 페이지 설정",
"description": "dialog text"
},
"single_batchexamples": {
"message": "일괄 처리가 지원됩니다, 예:",
"description": "Header text; single window"
@ -952,7 +1054,7 @@
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - 링크 추가",
"message": "TraitorousDownloading! - 링크 추가",
"description": "Title of single window"
},
"sizeB": {
@ -1123,6 +1225,14 @@
"message": "새 다운로드가 시작되지 않습니다",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "하위 폴더",
"description": "label text"
},
"subfolder_placeholder": {
"message": "다운로드 디렉토리 내의 이 하위 폴더에 파일을 저장합니다",
"description": "placeholder text within an input box"
},
"title": {
"message": "제목",
"description": "Column text; Title label (short)"
@ -1166,5 +1276,9 @@
"useonlyonce": {
"message": "한번만",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "사용자 취소",
"description": "Error message"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

164
_locales/ru/messages.json Normal file → Executable file
View File

@ -12,7 +12,7 @@
"description": "Action: Add paused"
},
"add_download": {
"message": "Добавить закачку",
"message": "добавить закачку",
"description": "Action for adding a download"
},
"add_new": {
@ -191,6 +191,22 @@
"message": "Удалить",
"description": "button text"
},
"deletefiles": {
"message": "Удалить файлы",
"description": "menu action"
},
"deletefiles_button": {
"message": "Удалить",
"description": "button text"
},
"deletefiles_text": {
"message": "Точно удалить эти файлы?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "Удаление файлов",
"description": "messagebox title"
},
"description": {
"message": "Описание",
"description": "Description (keep it short); e.g. the description column in select"
@ -216,27 +232,27 @@
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular": {
"message": "DownThemAll!",
"message": "TraitorousDownloading!",
"description": "Regular dta action; Menu text"
},
"dta_regular_all": {
"message": "DownThemAll! - Все вкладки",
"message": "TraitorousDownloading! - Все вкладки",
"description": "Menu text"
},
"dta_regular_image": {
"message": "Закачать изображение через DownThemAll!",
"message": "Закачать изображение через TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_link": {
"message": "Закачать ссылку через DownThemAll!",
"message": "Закачать ссылку через TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_media": {
"message": "Закачать медиа через DownThemAll!",
"message": "Закачать медиа через TraitorousDownloading!",
"description": "Menu text"
},
"dta_regular_selection": {
"message": "Закачать выделенное через DownThemAll!",
"message": "Закачать выделенное через TraitorousDownloading!",
"description": "Menu text"
},
"dta_turbo": {
@ -275,13 +291,37 @@
"message": "Неправильная ссылка",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "Полные пути к папкам не поддерживаются браузерами",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "Точки (.) в названии папок не поддерживаются браузерами",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "Ничего не выбрано",
"description": "Error Message; select window"
},
"export": {
"message": "Экспортировать в файл",
"description": "menu text"
},
"export_aria2": {
"message": "Экспортировать как список для aria2",
"description": "menu text"
},
"export_metalink": {
"message": "Экспортировать как Metalink",
"description": "menu text"
},
"export_text": {
"message": "Экспортировать как текст",
"description": "menu text"
},
"extensionDescription": {
"message": "Универсальная качалка для вашего браузера",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
"description": "TraitorousDownloading! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
},
"fastfiltering": {
"message": "Быстрый фильтр",
@ -331,6 +371,10 @@
"message": "Принудительный старт",
"description": "Menu text"
},
"import": {
"message": "Импортировать из файла",
"description": "menu text"
},
"information_title": {
"message": "Информация",
"description": "Used in message boxes"
@ -406,7 +450,7 @@
}
},
"manager_title": {
"message": "DownThemAll! Менеджер",
"message": "TraitorousDownloading! Менеджер",
"description": "Window/tab title"
},
"mask": {
@ -442,7 +486,7 @@
"description": "Action for moving a download up"
},
"nagging_message": {
"message": "Добавлено $DOWNLOADS$ закачек через DownThemAll! на текущий момент! Как постоянный пользователь подумайте над тем чтобы поддержать дальнейшую разработку. Спасибо!",
"message": "Добавлено $DOWNLOADS$ закачек через TraitorousDownloading! на текущий момент! Как постоянный пользователь подумайте над тем чтобы поддержать дальнейшую разработку. Спасибо!",
"description": "Donation nagging message; displayed as a notification bar in manager",
"placeholders": {
"downloads": {
@ -522,13 +566,33 @@
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "Настройки DownThemAll!",
"message": "Настройки TraitorousDownloading!",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "Добавлять закачки приостановленными, вместо того чтобы качать сразу",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "Кнопка TraitorousDownloading!",
"description": "label"
},
"pref_button_type_dta": {
"message": "TraitorousDownloading! выделенного",
"description": "label"
},
"pref_button_type_manager": {
"message": "Открыть менеджер",
"description": "label"
},
"pref_button_type_popup": {
"message": "Всплывающее меню",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "Настройки/Сеть",
"description": "Preferences/Network"
@ -537,10 +601,6 @@
"message": "Показывать уведомление когда список закачек завешен",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "Использовать OneClick! по умолчанию на кнопке в браузере",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "Не добавлять пункты в общее контекстное меню",
"description": "Preferences/General"
@ -549,6 +609,10 @@
"message": "Менеджер",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "Открывать менеджер в новом всплывающем окне",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "Показывать подсказки на вкладках менеджера",
"description": "Preferences/General"
@ -573,14 +637,42 @@
"message": "Удалять неудавшиеся закачки после перезапуска",
"description": "Preferences/General"
},
"pref_retries": {
"message": "Сколько раз пробовать перезапустить закачку при некритичных сбоях",
"description": "pref text"
},
"pref_retry_time": {
"message": "Как часто пробовать перезапустить закачку (в минутах)",
"description": "pref text"
},
"pref_show_urls": {
"message": "Показывать ссылки вместо имён",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "Проигрывать звуки",
"description": "checkbox text"
},
"pref_text_links": {
"message": "Пытаться обнаружить ссылки в тексте на сайте",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Тема:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Тёмная",
"description": "option text"
},
"pref_theme_default": {
"message": "Система/Браузер",
"description": "option text"
},
"pref_theme_light": {
"message": "Светлая",
"description": "option text"
},
"pref_ui": {
"message": "Интерфейс пользователя",
"description": "Preferences/General; group text"
@ -895,6 +987,20 @@
"message": "Продолжить",
"description": "Action for resuming a download"
},
"retrying": {
"message": "Перезапуск",
"description": "Status text"
},
"retrying_error": {
"message": "Перезапуск - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "В процессе",
"description": "Status text"
@ -920,7 +1026,7 @@
"description": "Menu text; select context"
},
"select_title": {
"message": "DownThemAll! - Выбрать ваши закачки",
"message": "TraitorousDownloading! - Выбрать ваши закачки",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
@ -943,6 +1049,18 @@
"message": "Задать маску переименования",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "Задать новую маску переименования",
"description": "dialog text"
},
"set_referrer": {
"message": "Задать реферера",
"description": "menu text"
},
"set_referrer_text": {
"message": "Задать нового реферера",
"description": "dialog text"
},
"single_batchexamples": {
"message": "Можно задать группы, к примеру:",
"description": "Header text; single window"
@ -952,7 +1070,7 @@
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - Добавить ссылку",
"message": "TraitorousDownloading! - Добавить ссылку",
"description": "Title of single window"
},
"sizeB": {
@ -1123,6 +1241,14 @@
"message": "Новые закачки запускаться не будут",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "Подпапка",
"description": "label text"
},
"subfolder_placeholder": {
"message": "Размещать файлы в подпапке выбранного пути для закачек",
"description": "placeholder text within an input box"
},
"title": {
"message": "Заголовок",
"description": "Column text; Title label (short)"
@ -1166,5 +1292,9 @@
"useonlyonce": {
"message": "Только сейчас",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "Отменено пользователем",
"description": "Error message"
}
}

1224
_locales/sv/messages.json Normal file

File diff suppressed because it is too large Load Diff

1300
_locales/tr/messages.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
"deffilter-aud": {
"label": "Audio",
"expr": "/\\.(?:mp3|wav|og(?:g|a)|flac|midi?|rm|aac|wma|mka|ape|opus)$/i",
"type": 1,
"type": 3,
"active": false,
"icon": "mp3"
},

View File

@ -1,8 +1,10 @@
{
"global-turbo": false,
"button-type": "popup",
"manager-in-popup": false,
"concurrent": 4,
"queue-notification": true,
"finish-notification": true,
"sounds": true,
"open-manager-on-queue": true,
"text-links": true,
"add-paused": false,
@ -13,6 +15,9 @@
"tooltip": true,
"show-urls": false,
"remove-missing-on-init": false,
"retries": 5,
"retry-time": 10,
"theme": "default",
"limits": [
{
"domain": "*",

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@ import { getManager } from "./manager/man";
import { select } from "./select";
import { single } from "./single";
import { Notification } from "./notifications";
import { MASK, FASTFILTER } from "./recentlist";
import { MASK, FASTFILTER, SUBFOLDER, SERVER } from "./recentlist";
import { openManager } from "./windowutils";
import { _ } from "./i18n";
@ -19,7 +19,10 @@ const MAX_BATCH = 10000;
export interface QueueOptions {
mask?: string;
subfolder?: string;
server?: string;
paused?: boolean;
cookies?: boolean;
}
export const API = new class APIImpl {
@ -28,10 +31,13 @@ export const API = new class APIImpl {
}
async queue(items: BaseItem[], options: QueueOptions) {
await MASK.init();
await Promise.all([MASK.init(), SUBFOLDER.init()]);
const {mask = MASK.current} = options;
const {subfolder = SUBFOLDER.current} = options;
const {server = SERVER.current} = options;
const {paused = false} = options;
const {cookies = false} = options;
const defaults: any = {
_idx: 0,
get idx() {
@ -46,8 +52,11 @@ export const API = new class APIImpl {
private: false,
postData: null,
mask,
subfolder,
server,
date: Date.now(),
paused
paused,
cookies,
};
let currentBatch = await Prefs.get("currentBatch", 0);
const initialBatch = currentBatch;
@ -77,7 +86,7 @@ export const API = new class APIImpl {
new Notification(null, _("queued-downloads", items.length));
}
}
if (await Prefs.get("open-manager-on-queue")) {
if (false && await Prefs.get("open-manager-on-queue")) {
await openManager(false);
}
}
@ -117,6 +126,14 @@ export const API = new class APIImpl {
await FASTFILTER.init();
await FASTFILTER.push(options.fast);
}
if (typeof options.subfolder === "string" && !options.subfolderOnce) {
await SUBFOLDER.init();
await SUBFOLDER.push(options.subfolder);
}
if (typeof options.server === "string" && !options.serverOnce) {
await SERVER.init();
await SERVER.push(options.server);
}
if (typeof options.type === "string") {
await Prefs.set("last-type", options.type);
}

View File

@ -22,9 +22,12 @@ import {
runtime,
history,
sessions,
// eslint-disable-next-line no-unused-vars
OnInstalled,
} from "./browser";
import { Bus } from "./bus";
import { filterInSitu } from "./util";
import { DB } from "./db";
const menus = typeof (_menus) !== "undefined" && _menus || _cmenus;
@ -104,19 +107,19 @@ class Handler {
async performSelection(options: SelectionOptions) {
try {
const toptions: any = {
const tabOptions: any = {
currentWindow: true,
discarded: false,
};
if (!CHROME) {
toptions.hidden = false;
tabOptions.hidden = false;
}
const selectedTabs = options.allTabs ?
await tabs.query(toptions) as any[] :
await tabs.query(tabOptions) as any[] :
[options.tab];
const textLinks = await Prefs.get("text-links", true);
const goptions = {
const gatherOptions = {
type: "DTA:gather",
selectionOnly: options.selectionOnly,
textLinks,
@ -125,7 +128,7 @@ class Handler {
};
const results = await Promise.all(selectedTabs.
map((tab: any) => runContentJob(tab, GATHER, goptions)));
map((tab: any) => runContentJob(tab, GATHER, gatherOptions)));
await this.processResults(options.turbo, results.flat());
}
@ -135,42 +138,40 @@ class Handler {
}
}
function getMajor(version?: string) {
if (!version) {
return "";
}
const match = version.match(/^\d+\.\d+/);
if (!match) {
return "";
}
return match[0];
}
runtime.onInstalled.addListener(({reason, previousVersion}: OnInstalled) => {
const {version} = runtime.getManifest();
const major = getMajor(version);
const prevMajor = getMajor(previousVersion);
if (reason === "update" && major !== prevMajor) {
// tabs.create({
// url: `https://about.downthemall.org/changelog/?cur=${major}&prev=${prevMajor}`,
// });
}
else if (reason === "install") {
// tabs.create({
// url: `https://about.downthemall.org/4.0/?cur=${major}`,
// });
}
});
locale.then(() => {
new class Action extends Handler {
constructor() {
super();
this.onClicked = this.onClicked.bind(this);
action.onClicked.addListener(this.onClicked);
}
async onClicked(tab: {id: number}) {
if (!tab.id) {
return;
}
try {
await this.processResults(
true,
await runContentJob(
tab, "/bundles/content-gather.js", {
type: "DTA:gather",
selectionOnly: false,
textLinks: await Prefs.get("text-links", true),
schemes: Array.from(ALLOWED_SCHEMES.values()),
transferable: TRANSFERABLE_PROPERTIES,
}));
}
catch (ex) {
console.error(ex);
}
}
}();
const menuHandler = new class Menus extends Handler {
constructor() {
super();
this.onClicked = this.onClicked.bind(this);
const alls = new Map<string, string[]>();
const mcreate = (options: any) => {
const menuCreate = (options: any) => {
if (CHROME) {
delete options.icons;
options.contexts = options.contexts.
@ -184,7 +185,7 @@ locale.then(() => {
}
menus.create(options);
};
mcreate({
menuCreate({
id: "DTARegularLink",
contexts: ["link"],
icons: {
@ -193,7 +194,7 @@ locale.then(() => {
},
title: _("dta.regular.link"),
});
mcreate({
menuCreate({
id: "DTATurboLink",
contexts: ["link"],
icons: {
@ -202,7 +203,7 @@ locale.then(() => {
},
title: _("dta.turbo.link"),
});
mcreate({
menuCreate({
id: "DTARegularImage",
contexts: ["image"],
icons: {
@ -211,7 +212,7 @@ locale.then(() => {
},
title: _("dta.regular.image"),
});
mcreate({
menuCreate({
id: "DTATurboImage",
contexts: ["image"],
icons: {
@ -220,7 +221,7 @@ locale.then(() => {
},
title: _("dta.turbo.image"),
});
mcreate({
menuCreate({
id: "DTARegularMedia",
contexts: ["video", "audio"],
icons: {
@ -229,7 +230,7 @@ locale.then(() => {
},
title: _("dta.regular.media"),
});
mcreate({
menuCreate({
id: "DTATurboMedia",
contexts: ["video", "audio"],
icons: {
@ -238,7 +239,7 @@ locale.then(() => {
},
title: _("dta.turbo.media"),
});
mcreate({
menuCreate({
id: "DTARegularSelection",
contexts: ["selection"],
icons: {
@ -247,7 +248,7 @@ locale.then(() => {
},
title: _("dta.regular.selection"),
});
mcreate({
menuCreate({
id: "DTATurboSelection",
contexts: ["selection"],
icons: {
@ -256,7 +257,7 @@ locale.then(() => {
},
title: _("dta.turbo.selection"),
});
mcreate({
menuCreate({
id: "DTARegular",
contexts: ["all", "browser_action", "tools_menu"],
icons: {
@ -265,7 +266,7 @@ locale.then(() => {
},
title: _("dta.regular"),
});
mcreate({
menuCreate({
id: "DTATurbo",
contexts: ["all", "browser_action", "tools_menu"],
icons: {
@ -274,12 +275,12 @@ locale.then(() => {
},
title: _("dta.turbo"),
});
mcreate({
menuCreate({
id: "sep-1",
contexts: ["all", "browser_action", "tools_menu"],
type: "separator"
});
mcreate({
menuCreate({
id: "DTARegularAll",
contexts: ["all", "browser_action", "tools_menu"],
icons: {
@ -288,7 +289,7 @@ locale.then(() => {
},
title: _("dta-regular-all"),
});
mcreate({
menuCreate({
id: "DTATurboAll",
contexts: ["all", "browser_action", "tools_menu"],
icons: {
@ -300,12 +301,12 @@ locale.then(() => {
const sep2ctx = menus.ACTION_MENU_TOP_LEVEL_LIMIT === 6 ?
["all", "tools_menu"] :
["all", "browser_action", "tools_menu"];
mcreate({
menuCreate({
id: "sep-2",
contexts: sep2ctx,
type: "separator"
});
mcreate({
menuCreate({
id: "DTAAdd",
contexts: ["all", "browser_action", "tools_menu"],
icons: {
@ -316,12 +317,12 @@ locale.then(() => {
},
title: _("add-download"),
});
mcreate({
menuCreate({
id: "sep-3",
contexts: ["all", "browser_action", "tools_menu"],
type: "separator"
});
mcreate({
/* menuCreate({
id: "DTAManager",
contexts: ["all", "browser_action", "tools_menu"],
icons: {
@ -329,8 +330,8 @@ locale.then(() => {
32: "/style/button-manager@2x.png",
},
title: _("manager.short"),
});
mcreate({
});*/
menuCreate({
id: "DTAPrefs",
contexts: ["all", "browser_action", "tools_menu"],
icons: {
@ -421,7 +422,7 @@ locale.then(() => {
}
}
async enumulate(action: string) {
async emulate(action: string) {
const tab = await tabs.query({
active: true,
currentWindow: true,
@ -543,34 +544,87 @@ locale.then(() => {
}
}();
Bus.on("do-regular", () => menuHandler.enumulate("DTARegular"));
Bus.on("do-regular-all", () => menuHandler.enumulate("DTARegularAll"));
Bus.on("do-turbo", () => menuHandler.enumulate("DTATurbo"));
Bus.on("do-turbo-all", () => menuHandler.enumulate("DTATurboAll"));
Bus.on("do-single", () => API.singleRegular(null));
Bus.on("open-manager", () => openManager(true));
Bus.on("open-prefs", () => openPrefs());
function adjustAction(globalTurbo: boolean) {
action.setPopup({
popup: globalTurbo ? "" : "/windows/popup.html"
new class Action extends Handler {
constructor() {
super();
this.onClicked = this.onClicked.bind(this);
action.onClicked.addListener(this.onClicked);
Prefs.get("button-type", false).then(v => this.adjust(v));
Prefs.on("button-type", (prefs, key, value) => {
this.adjust(value);
});
action.setIcon({
path: globalTurbo ? {
16: "/style/button-turbo.png",
32: "/style/button-turbo@2x.png",
} : {
}
adjust(type: string) {
action.setPopup({
popup: type !== "popup" ? "" : "/windows/popup.html"
});
let icons;
switch (type) {
case "popup":
icons = {
16: "/style/icon16.png",
32: "/style/icon32.png",
48: "/style/icon48.png",
64: "/style/icon64.png",
96: "/style/icon96.png",
128: "/style/icon128.png",
256: "/style/icon256.png"
};
break;
case "dta":
icons = {
16: "/style/button-regular.png",
32: "/style/button-regular@2x.png",
};
break;
case "turbo":
icons = {
16: "/style/button-turbo.png",
32: "/style/button-turbo@2x.png",
};
break;
case "manager":
icons = {
16: "/style/button-manager.png",
32: "/style/button-manager@2x.png",
};
break;
}
});
action.setIcon({path: icons});
}
async onClicked() {
switch (await Prefs.get("button-type")) {
case "popup":
break;
case "dta":
menuHandler.emulate("DTARegular");
break;
case "turbo":
menuHandler.emulate("DTATurbo");
break;
case "manager":
menuHandler.emulate("DTAManager");
break;
}
}
}();
Bus.on("do-regular", () => menuHandler.emulate("DTARegular"));
Bus.on("do-regular-all", () => menuHandler.emulate("DTARegularAll"));
Bus.on("do-turbo", () => menuHandler.emulate("DTATurbo"));
Bus.on("do-turbo-all", () => menuHandler.emulate("DTATurboAll"));
Bus.on("do-single", () => API.singleRegular(null));
Bus.on("open-manager", () => openManager(true));
Bus.on("open-prefs", () => openPrefs());
(async function init() {
const urlBase = runtime.getURL("");
history.onVisited.addListener(({url}: {url: string}) => {
@ -609,11 +663,14 @@ locale.then(() => {
await sessionRemover();
}
try {
await DB.init();
}
catch (ex) {
console.error("db init", ex.toString(), ex.message, ex.stack, ex);
}
await Prefs.set("last-run", new Date());
Prefs.get("global-turbo", false).then(v => adjustAction(v));
Prefs.on("global-turbo", (prefs, key, value) => {
adjustAction(value);
});
await filters();
await getManager();
})().catch(ex => {

View File

@ -73,7 +73,7 @@ class Numeral implements Generator {
this.digits = dir ? rawpieces[0].length : rawpieces[1].length;
this.length = Math.floor(
(this.stop - this.start + (dir ? 1 : -1)) / this.step);
this.preview = this[Symbol.iterator]().next().value;
this.preview = this[Symbol.iterator]().next().value as string;
Object.freeze(this);
}
@ -93,6 +93,60 @@ class Numeral implements Generator {
}
}
class Character implements Generator {
public readonly start: number;
public readonly stop: number;
public readonly step: number;
public readonly length: number;
public readonly preview: string;
constructor(str: string) {
const rawpieces = str.split(":").map(e => e.trim());
const pieces = rawpieces.map((e, i) => {
if (i === 2) {
return reallyParseInt(e);
}
if (e.length > 1) {
throw new Error("Malformed Character sequence");
}
return e.charCodeAt(0);
});
if (pieces.length < 2) {
throw new Error("Invalid input");
}
const [start, stop, step] = pieces;
if (step === 0) {
throw new Error("Invalid step");
}
this.step = !step ? 1 : step;
const dir = this.step > 0;
if (dir && start > stop) {
throw new Error("Invalid sequence");
}
else if (!dir && start < stop) {
throw new Error("Invalid sequence");
}
this.start = start;
this.stop = stop;
this.length = Math.floor(
(this.stop - this.start + (dir ? 1 : -1)) / this.step);
this.preview = this[Symbol.iterator]().next().value as string;
Object.freeze(this);
}
*[Symbol.iterator]() {
const {start, stop, step} = this;
const dir = step > 0;
for (let i = start; (dir ? i <= stop : i >= stop); i += step) {
yield String.fromCharCode(i);
}
}
}
export class BatchGenerator implements Generator {
private readonly gens: Generator[];
@ -120,11 +174,16 @@ export class BatchGenerator implements Generator {
try {
this.gens.push(new Numeral(tok));
}
catch (ex) {
catch {
try {
this.gens.push(new Character(tok));
}
catch {
this.gens.push(new Literal(`[${tok}]`));
this.hasInvalid = true;
}
}
}
if (str) {
this.gens.push(new Literal(str));
}

View File

@ -9,33 +9,33 @@ interface ExtensionListener {
}
export interface MessageSender {
tab?: Tab;
frameId?: number;
id?: number;
url?: string;
tlsChannelId?: string;
readonly tab?: Tab;
readonly frameId?: number;
readonly id?: number;
readonly url?: string;
readonly tlsChannelId?: string;
}
export interface Tab {
id?: number;
incognito?: boolean;
readonly id?: number;
readonly incognito?: boolean;
}
export interface MenuClickInfo {
menuItemId: string | number;
button?: number;
linkUrl?: string;
srcUrl?: string;
readonly menuItemId: string | number;
readonly button?: number;
readonly linkUrl?: string;
readonly srcUrl?: string;
}
export interface RawPort {
error: any;
name: string;
onDisconnect: ExtensionListener;
onMessage: ExtensionListener;
sender?: MessageSender;
readonly error: any;
readonly name: string;
readonly sender?: MessageSender;
readonly onDisconnect: ExtensionListener;
readonly onMessage: ExtensionListener;
disconnect: () => void;
postMessage: (message: any) => void;
}
@ -57,7 +57,7 @@ type Header = {name: string; value: string};
export interface DownloadOptions {
conflictAction: string;
filename: string;
filename?: string;
saveAs: boolean;
url: string;
method?: string;
@ -81,15 +81,23 @@ interface Downloads {
search(query: DownloadsQuery): Promise<any[]>;
getFileIcon(id: number, options?: any): Promise<string>;
setShelfEnabled(state: boolean): void;
onCreated: ExtensionListener;
onChanged: ExtensionListener;
onErased: ExtensionListener;
removeFile(manId: number): Promise<void>;
readonly onCreated: ExtensionListener;
readonly onChanged: ExtensionListener;
readonly onErased: ExtensionListener;
readonly onDeterminingFilename?: ExtensionListener;
}
interface WebRequest {
onBeforeSendHeaders: WebRequestListener;
onSendHeaders: WebRequestListener;
onHeadersReceived: WebRequestListener;
readonly onBeforeSendHeaders: WebRequestListener;
readonly onSendHeaders: WebRequestListener;
readonly onHeadersReceived: WebRequestListener;
}
export interface OnInstalled {
readonly reason: string;
readonly previousVersion?: string;
readonly temporary: boolean;
}
export const {browserAction} = polyfill;
@ -106,5 +114,7 @@ export const {tabs} = polyfill;
export const {webNavigation} = polyfill;
export const {webRequest}: {webRequest: WebRequest} = polyfill;
export const {windows} = polyfill;
export const {theme} = polyfill;
export const CHROME = navigator.appVersion.includes("Chrome/");
export const OPERA = navigator.appVersion.includes("OPR/");

View File

@ -8,32 +8,35 @@ import {runtime, tabs, RawPort, MessageSender} from "./browser";
export class Port extends EventEmitter {
private port: RawPort | null;
private disconnected = false;
constructor(port: RawPort) {
super();
this.port = port;
let disconnected = false;
const disconnect = () => {
if (disconnected) {
return;
}
disconnected = true;
this.port = null; // Break the cycle
this.emit("disconnect", this, port);
};
// Nasty firefox bug, thus listen for tab removal explicitly
if (port.sender && port.sender.tab && port.sender.tab.id) {
const otherTabId = port.sender.tab.id;
const tabListener = function(tabId: number) {
const tabListener = (tabId: number) => {
if (tabId !== otherTabId) {
return;
}
disconnect();
this.disconnect();
};
tabs.onRemoved.addListener(tabListener);
}
port.onMessage.addListener(this.onMessage.bind(this));
port.onDisconnect.addListener(disconnect);
port.onDisconnect.addListener(this.disconnect.bind(this));
}
disconnect() {
if (this.disconnected) {
return;
}
this.disconnected = true;
const {port} = this;
this.port = null; // Break the cycle
this.emit("disconnect", this, port);
}
get name() {
@ -120,6 +123,9 @@ export const Bus = new class extends EventEmitter {
port.disconnect();
return;
}
this.ports.emit(port.name, new Port(port));
const wrapped = new Port(port);
if (!this.ports.emit(port.name, wrapped)) {
wrapped.disconnect();
}
}
}();

147
lib/db.ts
View File

@ -1,14 +1,25 @@
"use strict";
// License: MIT
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
// License: MIT
// eslint-disable-next-line no-unused-vars
import { Download } from "./manager/download";
import { RUNNING, QUEUED, RETRYING } from "./manager/state";
import { storage } from "./browser";
import { sort } from "./sorting";
const VERSION = 1;
const STORE = "queue";
export const DB = new class DB {
interface Database {
init(): Promise<void>;
saveItems(items: Download[]): Promise<unknown>;
deleteItems(items: any[]): Promise<void>;
getAll(): Promise<BaseItem[]>;
}
export class IDB implements Database {
private db?: IDBDatabase;
constructor() {
@ -69,7 +80,7 @@ export const DB = new class DB {
return await new Promise(this.getAllInternal);
}
saveItemsInternal(items: any[], resolve: Function, reject: Function) {
saveItemsInternal(items: Download[], resolve: Function, reject: Function) {
if (!items || !items.length || !this.db) {
resolve();
return;
@ -83,9 +94,13 @@ export const DB = new class DB {
if (item.private) {
continue;
}
const req = store.put(item.toJSON());
const json = item.toJSON();
if (item.state === RUNNING || item.state === RETRYING) {
json.state = QUEUED;
}
const req = store.put(json);
if (!("dbId" in item) || item.dbId < 0) {
req.onsuccess = () => item.dbId = req.result;
req.onsuccess = () => item.dbId = req.result as number;
}
}
}
@ -94,12 +109,12 @@ export const DB = new class DB {
}
}
async saveItems(items: any[]) {
async saveItems(items: Download[]) {
await this.init();
return await new Promise(this.saveItemsInternal.bind(this, items));
}
deleteItemsInternal(items: any[], resolve: Function, reject: Function) {
deleteItemsInternal(items: any[], resolve: () => void, reject: Function) {
if (!items || !items.length || !this.db) {
resolve();
return;
@ -132,4 +147,120 @@ export const DB = new class DB {
await this.init();
await new Promise(this.deleteItemsInternal.bind(this, items));
}
}
class StorageDB implements Database {
private counter = 1;
async init(): Promise<void> {
const {db = null} = await storage.local.get("db");
if (!db || !db.counter) {
return;
}
this.counter = db.counter;
}
async saveItems(items: Download[]) {
const db: any = {items: []};
for (const item of items) {
if (!item.dbId) {
item.dbId = ++this.counter;
}
db.items.push(item.toJSON());
}
db.counter = this.counter;
await storage.local.set({db});
}
async deleteItems(items: any[]): Promise<void> {
const gone = new Set(items.map(i => i.dbId));
const {db = null} = await storage.local.get("db");
if (!db) {
return;
}
db.items = db.items.filter((i: any) => !gone.has(i.dbId));
await storage.local.set({db});
}
async getAll() {
const {db = null} = await storage.local.get("db");
if (!db || !Array.isArray(db.items)) {
return [];
}
return sort(db.items, (i: any) => i.position) as BaseItem[];
}
}
class MemoryDB implements Database {
private counter = 1;
private items = new Map();
init(): Promise<void> {
return Promise.resolve();
}
saveItems(items: Download[]) {
for (const item of items) {
if (item.private) {
continue;
}
if (!item.dbId) {
item.dbId = ++this.counter;
}
this.items.set(item.dbId, item.toJSON());
}
return Promise.resolve();
}
deleteItems(items: any[]) {
for (const item of items) {
if (!("dbId" in item)) {
continue;
}
this.items.delete(item.dbId);
}
return Promise.resolve();
}
getAll(): Promise<BaseItem[]> {
return Promise.resolve(Array.from(this.items.values()));
}
}
export const DB = new class DBWrapper implements Database {
saveItems(items: Download[]): Promise<unknown> {
return this.db.saveItems(items);
}
deleteItems(items: any[]): Promise<void> {
return this.db.deleteItems(items);
}
getAll(): Promise<BaseItem[]> {
return this.db.getAll();
}
private db: Database;
async init() {
try {
this.db = new IDB();
await this.db.init();
}
catch (ex) {
console.warn(
"Failed to initialize idb backend, using storage db fallback", ex);
try {
this.db = new StorageDB();
await this.db.init();
}
catch (ex) {
console.warn(
"Failed to initialize storage backend, using memory db fallback", ex);
this.db = new MemoryDB();
await this.db.init();
}
}
}
}();

View File

@ -4,7 +4,7 @@
import {memoize} from "./memoize";
import langs from "../_locales/all.json";
import { sorted, naturalCaseCompare } from "./sorting";
import lf from "localforage";
export const ALL_LANGS = Object.freeze(new Map<string, string>(
sorted(Object.entries(langs), e => {
@ -40,11 +40,11 @@ class Entry {
this.message = entry.message.replace(/\$[A-Z0-9]+\$/g, (r: string) => {
hit = true;
const id = r.substr(1, r.length - 2).toLocaleLowerCase();
const pholder = entry.placeholders[id];
if (!pholder || !pholder.content) {
const placeholder = entry.placeholders[id];
if (!placeholder || !placeholder.content) {
throw new Error(`Invalid placeholder: ${id}`);
}
return `${pholder.content}$`;
return `${placeholder.content}$`;
});
if (!hit) {
throw new Error("Not entry-able");
@ -123,15 +123,18 @@ async function fetchLanguage(code: string) {
}
function loadCached() {
if (document.location.pathname.includes("/windows/")) {
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
return JSON.parse(cached) as any[];
}
}
async function loadCached(): Promise<any> {
const cached = await lf.getItem<string>(CACHE_KEY);
if (!cached) {
return null;
}
const parsed = JSON.parse(cached);
if (!Array.isArray(parsed) || !parsed[0].CRASH || !parsed[0].CRASH.message) {
console.warn("rejecting cached locales", parsed);
return null;
}
return parsed;
}
async function loadRawLocales() {
// en is the base locale, always to be loaded
@ -187,16 +190,16 @@ async function load(): Promise<Localization> {
}
CURRENT = currentLang;
// en is the base locale
let valid = loadCached();
let valid = await loadCached();
if (!valid) {
valid = await loadRawLocales();
localStorage.setItem(CACHE_KEY, JSON.stringify(valid));
await lf.setItem(CACHE_KEY, JSON.stringify(valid));
}
if (!valid.length) {
throw new Error("Could not lood ANY of these locales");
throw new Error("Could not load ANY of these locales");
}
const custom = localStorage.getItem(CUSTOM_KEY);
const custom = await lf.getItem<string>(CUSTOM_KEY);
if (custom) {
try {
valid.push(JSON.parse(custom));
@ -239,7 +242,7 @@ locale.then(l => {
/**
* Localize a message
* @param {string} id Identifier of the string to localize
* @param {string[]} [subst] Message substituations
* @param {string[]} [subst] Message substitutions
* @returns {string} Localized message
*/
export function _(id: string, ...subst: any[]) {
@ -302,11 +305,11 @@ export async function localize<T extends HTMLElement | DocumentFragment>(
return localize_(elem);
}
export function saveCustomLocale(data?: string) {
export async function saveCustomLocale(data?: string) {
if (!data) {
localStorage.removeItem(CUSTOM_KEY);
await lf.removeItem(CUSTOM_KEY);
return;
}
new Localization(JSON.parse(data));
localStorage.setItem(CUSTOM_KEY, data);
await localStorage.setItem(CUSTOM_KEY, data);
}

View File

@ -4,9 +4,11 @@
import { downloads, CHROME } from "./browser";
import { EventEmitter } from "../uikit/lib/events";
import { PromiseSerializer } from "./pserializer";
import lf from "localforage";
const VERSION = 1;
const STORE = "iconcache";
// eslint-disable-next-line no-magic-numbers
const CACHE_SIZES = CHROME ? [16, 32] : [16, 32, 64, 127];
@ -48,37 +50,17 @@ const SYNONYMS = Object.freeze(new Map<string, string>([
]));
export const IconCache = new class IconCache extends EventEmitter {
private db: Promise<IDBDatabase>;
private db = lf.createInstance({name: STORE});
private cache: Map<string, string>;
constructor() {
super();
this.db = this.init();
this.cache = new Map();
this.get = PromiseSerializer.wrapNew(8, this, this.get);
this.set = PromiseSerializer.wrapNew(1, this, this.set);
}
private async init() {
return await new Promise<IDBDatabase>((resolve, reject) => {
const req = indexedDB.open(STORE, VERSION);
req.onupgradeneeded = evt => {
const db = req.result;
switch (evt.oldVersion) {
case 0: {
db.createObjectStore(STORE);
break;
}
}
};
req.onerror = ex => reject(ex);
req.onsuccess = () => {
resolve(req.result);
};
});
}
private normalize(ext: string) {
ext = ext.toLocaleLowerCase();
return SYNONYMS.get(ext) || ext;
@ -95,36 +77,25 @@ export const IconCache = new class IconCache extends EventEmitter {
if (rv) {
return rv;
}
const db = await this.db;
rv = this.cache.get(sext);
if (rv) {
return rv;
}
return await new Promise<string | undefined>(resolve => {
const trans = db.transaction(STORE, "readonly");
trans.onerror = () => resolve(undefined);
const store = trans.objectStore(STORE);
const req = store.get(sext);
req.onerror = () => resolve(undefined);
req.onsuccess = () => {
const rv = this.cache.get(sext);
if (rv) {
resolve(rv);
return;
}
let {result} = req;
let result = await this.db.getItem<any>(sext);
if (!result) {
resolve(undefined);
return;
return this.cache.get(sext);
}
if (typeof req.result !== "string") {
rv = this.cache.get(sext);
if (rv) {
return rv;
}
if (typeof result !== "string") {
result = URL.createObjectURL(result).toString();
}
this.cache.set(sext, result);
this.cache.set(ext, "");
resolve(result);
};
});
return result;
}
async set(ext: string, manId: number) {
@ -145,18 +116,9 @@ export const IconCache = new class IconCache extends EventEmitter {
}
for (const {size, icon} of urls) {
this.cache.set(`${ext}-${size}`, URL.createObjectURL(icon));
await this.db.setItem(`${ext}-${size}`, icon);
}
this.cache.set(ext, "");
const db = await this.db;
await new Promise((resolve, reject) => {
const trans = db.transaction(STORE, "readwrite");
trans.onerror = reject;
trans.oncomplete = resolve;
const store = trans.objectStore(STORE);
for (const {size, icon} of urls) {
store.put(icon, `${ext}-${size}`);
}
});
this.emit("cached", ext);
}
}();

261
lib/imex.ts Normal file
View File

@ -0,0 +1,261 @@
"use strict";
// License: MIT
import { getTextLinks } from "./textlinks";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
import { ALLOWED_SCHEMES } from "./constants";
export const NS_METALINK_RFC5854 = "urn:ietf:params:xml:ns:metalink";
export const NS_DTA = "http://www.downthemall.net/properties#";
function parseNum(
file: Element,
attr: string,
defaultValue: number,
ns = NS_METALINK_RFC5854) {
const val = file.getAttributeNS(ns, attr);
if (!val) {
return defaultValue + 1;
}
const num = parseInt(val, 10);
if (isFinite(num)) {
return num;
}
return defaultValue + 1;
}
function importMeta4(data: string) {
const parser = new DOMParser();
const document = parser.parseFromString(data, "text/xml");
const {documentElement} = document;
const items: BaseItem[] = [];
let batch = 0;
for (const file of documentElement.querySelectorAll("file")) {
try {
const url = Array.from(file.querySelectorAll("url")).map(u => {
try {
const {textContent} = u;
if (!textContent) {
return null;
}
const url = new URL(textContent);
if (!ALLOWED_SCHEMES.has(url.protocol)) {
return null;
}
const prio = parseNum(u, "priority", 0);
return {
url,
prio
};
}
catch {
return null;
}
}).filter(u => !!u).reduce((p, c) => {
if (!c) {
return null;
}
if (!p || p.prio < c.prio) {
return c;
}
return p;
});
if (!url) {
continue;
}
batch = parseNum(file, "num", batch, NS_DTA);
const idx = parseNum(file, "idx", 0, NS_DTA);
const item: BaseItem = {
url: url.url.toString(),
usable: decodeURIComponent(url.url.toString()),
batch,
idx
};
const ref = file.getAttributeNS(NS_DTA, "referrer");
if (ref) {
item.referrer = ref;
item.usableReferrer = decodeURIComponent(ref);
}
const mask = file.getAttributeNS(NS_DTA, "mask");
if (mask) {
item.mask = mask;
}
const description = file.querySelector("description");
if (description && description.textContent) {
item.description = description.textContent.trim();
}
const title = file.getElementsByTagNameNS(NS_DTA, "title");
if (title && title[0] && title[0].textContent) {
item.title = title[0].textContent;
}
items.push(item);
}
catch (ex) {
console.error("Failed to import file", ex);
}
}
return items;
}
function parseKV(current: BaseItem, line: string) {
const [k, v] = line.split("=", 2);
switch (k.toLocaleLowerCase().trim()) {
case "referer": {
const refererUrls = getTextLinks(v);
if (refererUrls && refererUrls.length) {
current.referrer = refererUrls.pop();
current.usableReferrer = decodeURIComponent(current.referrer || "");
}
break;
}
}
}
export function importText(data: string) {
if (data.includes(NS_METALINK_RFC5854)) {
return importMeta4(data);
}
const splitter = /((?:.|\r)+)\n|(.+)$/g;
const spacer = /^\s+/;
let match;
let current: BaseItem | undefined = undefined;
let idx = 0;
const items = [];
while ((match = splitter.exec(data)) !== null) {
try {
const line = match[0].trimRight();
if (!line) {
continue;
}
if (spacer.test(line)) {
if (!current) {
continue;
}
parseKV(current, line);
continue;
}
const urls = getTextLinks(line);
if (!urls || !urls.length) {
continue;
}
current = {
url: urls[0],
usable: decodeURIComponent(urls[0]),
idx: ++idx
};
items.push(current);
}
catch (ex) {
current = undefined;
console.error("Failed to import", ex);
}
}
return items;
}
export interface Exporter {
fileName: string;
getText(items: BaseItem[]): string;
}
class TextExporter {
readonly fileName: string;
constructor() {
this.fileName = "links.txt";
}
getText(items: BaseItem[]) {
const lines = [];
for (const item of items) {
lines.push(item.url);
}
return lines.join("\n");
}
}
class Aria2Exporter {
readonly fileName: string;
constructor() {
this.fileName = "links.aria2.txt";
}
getText(items: BaseItem[]) {
const lines = [];
for (const item of items) {
lines.push(item.url);
if (item.referrer) {
lines.push(` referer=${item.referrer}`);
}
}
return lines.join("\n");
}
}
class MetalinkExporter {
readonly fileName: string;
constructor() {
this.fileName = "links.meta4";
}
getText(items: BaseItem[]) {
const document = window.document.implementation.
createDocument(NS_METALINK_RFC5854, "metalink", null);
const root = document.documentElement;
root.setAttributeNS(NS_DTA, "generator", "TraitorousDownloading!");
root.appendChild(document.createComment(
"metalink as exported by TraitorousDownloading!",
));
for (const item of items) {
const anyItem = item as any;
const f = document.createElementNS(NS_METALINK_RFC5854, "file");
f.setAttribute("name", anyItem.currentName);
if (item.batch) {
f.setAttributeNS(NS_DTA, "num", item.batch.toString());
}
if (item.idx) {
f.setAttributeNS(NS_DTA, "idx", item.idx.toString());
}
if (item.referrer) {
f.setAttributeNS(NS_DTA, "referrer", item.referrer);
}
if (item.mask) {
f.setAttributeNS(NS_DTA, "mask", item.mask);
}
if (item.description) {
const n = document.createElementNS(NS_METALINK_RFC5854, "description");
n.textContent = item.description;
f.appendChild(n);
}
if (item.title) {
const n = document.createElementNS(NS_DTA, "title");
n.textContent = item.title;
f.appendChild(n);
}
const u = document.createElementNS(NS_METALINK_RFC5854, "url");
u.textContent = item.url;
f.appendChild(u);
if (anyItem.totalSize > 0) {
const s = document.createElementNS(NS_METALINK_RFC5854, "size");
s.textContent = anyItem.totalSize.toString();
f.appendChild(s);
}
root.appendChild(f);
}
let xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
xml += root.outerHTML;
return xml;
}
}
export const textExporter = new TextExporter();
export const aria2Exporter = new Aria2Exporter();
export const metalinkExporter = new MetalinkExporter();

4
lib/ipreg.ts Normal file
View File

@ -0,0 +1,4 @@
"use strict";
// License: MIT
export const IPReg = /^(?:(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$|^(?:(?:(?:[0-9a-fA-F]{1,4}):){7}(?:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){6}(?:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){5}(?::((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,2}|:)|(?:(?:[0-9a-fA-F]{1,4}):){4}(?:(:(?:[0-9a-fA-F]{1,4})){0,1}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,3}|:)|(?:(?:[0-9a-fA-F]{1,4}):){3}(?:(:(?:[0-9a-fA-F]{1,4})){0,2}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,4}|:)|(?:(?:[0-9a-fA-F]{1,4}):){2}(?:(:(?:[0-9a-fA-F]{1,4})){0,3}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,5}|:)|(?:(?:[0-9a-fA-F]{1,4}):){1}(?:(:(?:[0-9a-fA-F]{1,4})){0,4}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,6}|:)|(?::((?::(?:[0-9a-fA-F]{1,4})){0,5}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(?::(?:[0-9a-fA-F]{1,4})){1,7}|:)))(%[0-9a-zA-Z]{1,})?$/;

View File

@ -15,6 +15,7 @@ export interface BaseItem {
batch?: number;
idx: number;
mask?: string;
subfolder?: string;
startDate?: number;
private?: boolean;
postData?: string;
@ -27,10 +28,12 @@ const OPTIONPROPS = Object.freeze([
"fileName",
"batch", "idx",
"mask",
"subfolder",
"startDate",
"private",
"postData",
"paused"
"paused",
"server", "cookies",
]);
function maybeAssign(options: any, what: any) {

View File

@ -5,6 +5,8 @@
import { parsePath, URLd } from "../util";
import { QUEUED, RUNNING, PAUSED } from "./state";
import Renamer from "./renamer";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "../item";
const SAVEDPROPS = [
"state",
@ -14,6 +16,7 @@ const SAVEDPROPS = [
"usableReferrer",
"fileName",
"mask",
"subfolder",
"date",
// batches
"batch",
@ -48,7 +51,9 @@ const DEFAULTS = {
written: 0,
manId: 0,
mime: "",
prerolled: false
prerolled: false,
retries: 0,
deadline: 0
};
let sessionId = 0;
@ -103,9 +108,13 @@ export class BaseDownload {
public mask: string;
public subfolder: string;
public prerolled: boolean;
constructor(options: any) {
public retries: number;
constructor(options: BaseItem) {
Object.assign(this, DEFAULTS);
this.assign(options);
if (this.state === RUNNING) {
@ -113,14 +122,16 @@ export class BaseDownload {
}
this.sessionId = ++sessionId;
this.renamer = new Renamer(this);
this.retries = 0;
}
assign(options: any) {
assign(options: BaseItem) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self: any = this;
const other: any = options;
for (const prop of SAVEDPROPS) {
if (prop in options) {
self[prop] = options[prop];
self[prop] = other[prop];
}
}
this.uURL = new URL(this.url) as URLd;
@ -180,8 +191,10 @@ export class BaseDownload {
rv.destPath = dest.path;
rv.destFull = dest.full;
rv.currentName = this.browserName || rv.destName || rv.finalName;
rv.currentFull = `${dest.path}/${rv.currentName}`;
rv.error = this.error;
rv.ext = this.renamer.p_ext;
rv.retries = this.retries;
return rv;
}
}

View File

@ -3,7 +3,7 @@
// eslint-disable-next-line no-unused-vars
import { CHROME, downloads, DownloadOptions } from "../browser";
import { Prefs } from "../prefs";
import { Prefs, PrefWatcher } from "../prefs";
import { PromiseSerializer } from "../pserializer";
import { filterInSitu, parsePath } from "../util";
import { BaseDownload } from "./basedownload";
@ -16,12 +16,27 @@ import {
DONE,
FORCABLE,
MISSING,
PAUSABLE,
PAUSEABLE,
PAUSED,
QUEUED,
RUNNING
RUNNING,
RETRYING
} from "./state";
import { Preroller } from "./preroller";
// eslint-disable-next-line no-unused-vars
import { Preroller, PrerollResults } from "./preroller";
function isRecoverable(error: string) {
switch (error) {
case "SERVER_FAILED":
return true;
default:
return error.startsWith("NETWORK_");
}
}
const RETRIES = new PrefWatcher("retries", 5);
const RETRY_TIME = new PrefWatcher("retry-time", 5);
export class Download extends BaseDownload {
public manager: Manager;
@ -34,6 +49,10 @@ export class Download extends BaseDownload {
public error: string;
public dbId: number;
public deadline: number;
constructor(manager: Manager, options: any) {
super(options);
this.manager = manager;
@ -86,6 +105,7 @@ export class Download extends BaseDownload {
return;
}
catch (ex) {
console.error("cannot resume", ex);
this.manager.removeManId(this.manId);
this.removeFromBrowser();
}
@ -111,11 +131,13 @@ export class Download extends BaseDownload {
}
const options: DownloadOptions = {
conflictAction: await Prefs.get("conflict-action"),
filename: this.dest.full,
saveAs: false,
url: this.url,
headers: [],
};
if (!CHROME) {
options.filename = this.dest.full;
}
if (!CHROME && this.private) {
options.incognito = true;
}
@ -175,16 +197,7 @@ export class Download extends BaseDownload {
if (!res) {
return;
}
if (res.mime) {
this.mime = res.mime;
}
if (res.name) {
this.serverName = res.name;
}
if (res.error) {
this.cancel();
this.error = res.error;
}
this.adoptPrerollResults(res);
}
catch (ex) {
console.error("Failed to preroll", this, ex.toString(), ex.stack, ex);
@ -197,6 +210,18 @@ export class Download extends BaseDownload {
}
}
adoptPrerollResults(res: PrerollResults) {
if (res.mime) {
this.mime = res.mime;
}
if (res.name) {
this.serverName = res.name;
}
if (res.error) {
this.cancelAccordingToError(res.error);
}
}
resume(forced = false) {
if (!(FORCABLE & this.state)) {
return;
@ -209,20 +234,32 @@ export class Download extends BaseDownload {
}
}
async pause() {
if (!(PAUSABLE & this.state)) {
async pause(retry?: boolean) {
if (!(PAUSEABLE & this.state)) {
return;
}
if (!retry) {
this.retries = 0;
this.deadline = 0;
}
else {
// eslint-disable-next-line no-magic-numbers
this.deadline = Date.now() + RETRY_TIME.value * 60 * 1000;
}
if (this.state === RUNNING && this.manId) {
try {
await downloads.pause(this.manId);
}
catch (ex) {
console.error("pause", ex.toString(), ex);
this.cancel();
return;
}
}
this.changeState(PAUSED);
this.changeState(retry ? RETRYING : PAUSED);
}
reset() {
@ -230,6 +267,8 @@ export class Download extends BaseDownload {
this.manId = 0;
this.written = this.totalSize = 0;
this.mime = this.serverName = this.browserName = "";
this.retries = 0;
this.deadline = 0;
}
async removeFromBrowser() {
@ -238,7 +277,7 @@ export class Download extends BaseDownload {
await downloads.cancel(id);
}
catch (ex) {
// ingored
// ignored
}
await new Promise(r => setTimeout(r, 1000));
try {
@ -246,7 +285,7 @@ export class Download extends BaseDownload {
}
catch (ex) {
console.error(id, ex.toString(), ex);
// ingored
// ignored
}
}
@ -262,6 +301,17 @@ export class Download extends BaseDownload {
this.changeState(CANCELED);
}
async cancelAccordingToError(error: string) {
if (!isRecoverable(error) || ++this.retries > RETRIES.value) {
this.cancel();
this.error = error;
return;
}
await this.pause(true);
this.error = error;
}
setMissing() {
if (this.manId) {
this.manager.removeManId(this.manId);
@ -318,8 +368,7 @@ export class Download extends BaseDownload {
this.changeState(PAUSED);
}
else if (error) {
this.cancel();
this.error = error;
this.cancelAccordingToError(error);
}
else {
this.changeState(RUNNING);
@ -330,6 +379,9 @@ export class Download extends BaseDownload {
if (state.paused) {
this.changeState(PAUSED);
}
else if (error) {
this.cancelAccordingToError(error);
}
else {
this.cancel();
this.error = error || "";
@ -346,4 +398,27 @@ export class Download extends BaseDownload {
this.setMissing();
}
}
updateFromSuggestion(state: any) {
const res: PrerollResults = {};
if (state.mime) {
res.mime = state.mime;
}
if (state.filename) {
res.name = state.filename;
}
if (state.finalUrl) {
res.finalURL = state.finalUrl;
const detected = Preroller.maybeFindNameFromSearchParams(this, res);
if (detected) {
res.name = detected;
}
}
try {
this.adoptPrerollResults(res);
}
finally {
this.markDirty();
}
}
}

View File

@ -4,11 +4,11 @@
import { EventEmitter } from "../events";
import { Notification } from "../notifications";
import { DB } from "../db";
import { QUEUED, CANCELED, RUNNING } from "./state";
import { QUEUED, CANCELED, RUNNING, RETRYING } from "./state";
// eslint-disable-next-line no-unused-vars
import { Bus, Port } from "../bus";
import { sort } from "../sorting";
import { Prefs } from "../prefs";
import { Prefs, PrefWatcher } from "../prefs";
import { _ } from "../i18n";
import { CoalescedUpdate, mapFilterInSitu, filterInSitu } from "../util";
import { PromiseSerializer } from "../pserializer";
@ -16,7 +16,8 @@ import { Download } from "./download";
import { ManagerPort } from "./port";
import { Scheduler } from "./scheduler";
import { Limits } from "./limits";
import { downloads, runtime, webRequest, CHROME } from "../browser";
import { downloads, runtime, webRequest, CHROME, OPERA } from "../browser";
import { browser } from "webextension-polyfill-ts";
const US = runtime.getURL("");
@ -30,6 +31,9 @@ const setShelfEnabled = downloads.setShelfEnabled || function() {
// ignored
};
const FINISH_NOTIFICATION = new PrefWatcher("finish-notification", true);
const SOUNDS = new PrefWatcher("sounds", false);
export class Manager extends EventEmitter {
private items: Download[];
@ -49,11 +53,18 @@ export class Manager extends EventEmitter {
private readonly running: Set<Download>;
private readonly retrying: Set<Download>;
private scheduler: Scheduler | null;
private shouldReload: boolean;
private deadlineTimer: number;
constructor() {
if (!document.location.href.includes("background")) {
throw new Error("Not on background");
}
super();
this.active = true;
this.shouldReload = false;
@ -63,23 +74,30 @@ export class Manager extends EventEmitter {
AUTOSAVE_TIMEOUT, this.save.bind(this));
this.dirty = new CoalescedUpdate(
DIRTY_TIMEOUT, this.processDirty.bind(this));
this.processDeadlines = this.processDeadlines.bind(this);
this.sids = new Map();
this.manIds = new Map();
this.ports = new Set();
this.scheduler = null;
this.running = new Set();
this.retrying = new Set();
this.startNext = PromiseSerializer.wrapNew(1, this, this.startNext);
downloads.onChanged.addListener(this.onChanged.bind(this));
downloads.onErased.addListener(this.onErased.bind(this));
if (CHROME && downloads.onDeterminingFilename) {
downloads.onDeterminingFilename.addListener(
this.onDeterminingFilename.bind(this));
}
Bus.onPort("manager", (port: Port) => {
const mport = new ManagerPort(this, port);
const managerPort = new ManagerPort(this, port);
port.on("disconnect", () => {
this.ports.delete(mport);
this.ports.delete(managerPort);
});
this.ports.add(mport);
this.ports.add(managerPort);
return true;
});
Limits.on("changed", () => {
this.resetScheduler();
@ -109,7 +127,7 @@ export class Manager extends EventEmitter {
// Do not wait for the scheduler
this.resetScheduler();
this.emit("inited");
this.emit("initialized");
setTimeout(() => this.checkMissing(), MISSING_TIMEOUT);
runtime.onUpdateAvailable.addListener(() => {
if (this.running.size) {
@ -148,6 +166,20 @@ export class Manager extends EventEmitter {
this.manIds.delete(downloadId);
}
onDeterminingFilename(state: any, suggest: Function) {
const download = this.manIds.get(state.id);
if (!download) {
return;
}
try {
download.updateFromSuggestion(state);
}
finally {
const suggestion = {filename: download.dest.full};
suggest(suggestion);
}
}
async resetScheduler() {
this.scheduler = null;
await this.startNext();
@ -188,14 +220,11 @@ export class Manager extends EventEmitter {
this.notifiedFinished = false;
}
async maybeRunFinishActions() {
if (this.running.size) {
return;
}
await this.maybeNotifyFinished();
maybeRunFinishActions() {
if (this.running.size) {
return;
}
this.maybeNotifyFinished();
if (this.shouldReload) {
this.saveQueue.trigger();
setTimeout(() => {
@ -208,15 +237,21 @@ export class Manager extends EventEmitter {
setShelfEnabled(true);
}
async maybeNotifyFinished() {
if (!(await Prefs.get("finish-notification"))) {
maybeNotifyFinished() {
if (this.notifiedFinished || this.running.size || this.retrying.size) {
return;
}
if (this.notifiedFinished || this.running.size) {
return;
if (SOUNDS.value && !OPERA) {
const audio = new Audio(runtime.getURL("/style/done.opus"));
audio.addEventListener("canplaythrough", () => audio.play());
audio.addEventListener("ended", () => document.body.removeChild(audio));
audio.addEventListener("error", () => document.body.removeChild(audio));
document.body.appendChild(audio);
}
if (FINISH_NOTIFICATION.value) {
new Notification(null, _("queue-finished"));
}
this.notifiedFinished = true;
new Notification(null, _("queue-finished"));
}
addManId(id: number, download: Download) {
@ -227,6 +262,61 @@ export class Manager extends EventEmitter {
this.manIds.delete(id);
}
async prepareItems(items: any[]) {
var links = new Array();
for (var item of items) {
var cookiesToSend = Array();
var cs = await browser.cookies.getAll({
url: item.url,
firstPartyDomain: null,
});
if (item.cookies) {
for (var c of cs){
cookiesToSend.push({
name: c.name,
value: c.value,
domain: c.domain,
// expires: new Date(c.expirationDate * 1000) ?? null,
path: c.path,
secure: c.secure,
httponly: c.httpOnly,
// samesite: c.sameSite,
});
}
}
var status = item.paused ? "Paused":"Queue";
links.push({
description: item.description,
filename: item.fileName,
mask: item.mask,
status: status,
postData: item.postData,
subdir: item.subfolder,
title: item.title,
url: item.usable,
referrer: item.usableReferrer,
cookies: cookiesToSend,
});
}
return links;
}
addNewDownloads(items: any[]) {
if (!items || !items.length) {
return;
}
this.prepareItems(items).then(links => {
var xhr = new XMLHttpRequest();
xhr.open("POST", items[0].server);
xhr.setRequestHeader("Content-Type", "application/javascript");
xhr.send(JSON.stringify(links));
});
}
/*
addNewDownloads(items: any[]) {
if (!items || !items.length) {
return;
@ -249,6 +339,7 @@ export class Manager extends EventEmitter {
this.save(items);
this.startNext();
}
*/
setDirty(item: Download) {
this.dirty.add(item);
@ -315,35 +406,86 @@ export class Manager extends EventEmitter {
if (oldState === RUNNING) {
this.running.delete(download);
}
else if (oldState === RETRYING) {
this.retrying.delete(download);
this.findDeadline();
}
if (newState === QUEUED) {
this.resetScheduler();
this.startNext().catch(console.error);
}
else if (newState === RUNNING) {
// Usually we already added it. Buit if a user uses the built-in
// download manager to resart
// Usually we already added it. But if a user uses the built-in
// download manager to restart
// a download, we have not, so make sure it is added either way
this.running.add(download);
}
else {
if (newState === RETRYING) {
this.addRetry(download);
}
this.startNext().catch(console.error);
}
}
addRetry(download: Download) {
this.retrying.add(download);
this.findDeadline();
}
private findDeadline() {
let deadline = Array.from(this.retrying).
reduce<number>((deadline, item) => {
if (deadline) {
return item.deadline ? Math.min(deadline, item.deadline) : deadline;
}
return item.deadline;
}, 0);
if (deadline <= 0) {
return;
}
deadline -= Date.now();
if (deadline <= 0) {
return;
}
if (this.deadlineTimer) {
window.clearTimeout(this.deadlineTimer);
}
this.deadlineTimer = window.setTimeout(this.processDeadlines, deadline);
}
private processDeadlines() {
this.deadlineTimer = 0;
try {
const now = Date.now();
this.items.forEach(item => {
if (item.deadline && Math.abs(item.deadline - now) < 1000) {
this.retrying.delete(item);
item.resume(false);
}
});
}
finally {
this.findDeadline();
}
}
sorted(sids: number[]) {
try {
// Construct new items
const csids = new Map(this.sids);
const currentSids = new Map(this.sids);
let items = mapFilterInSitu(sids, sid => {
const item = csids.get(sid);
const item = currentSids.get(sid);
if (!item) {
return null;
}
csids.delete(sid);
currentSids.delete(sid);
return item;
}, e => !!e);
if (csids.size) {
items = items.concat(sort(Array.from(csids.values()), i => i.position));
if (currentSids.size) {
items = items.concat(
sort(Array.from(currentSids.values()), i => i.position));
}
this.items = items;
this.setPositions();

View File

@ -9,6 +9,8 @@ import { BaseDownload } from "./basedownload";
import { Manager } from "./man";
// eslint-disable-next-line no-unused-vars
import { Port } from "../bus";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "../item";
type SID = {sid: number};
type SIDS = {
@ -42,6 +44,9 @@ export class ManagerPort {
port.on("prefs", () => {
openPrefs();
});
port.on("import", ({items}: {items: BaseItem[]}) => {
API.regular(items, []);
});
port.on("all", () => this.sendAll());
port.on("removeSids", this.onMsgRemoveSids);
port.on("showSingle", async () => {

View File

@ -54,6 +54,9 @@ export class Preroller {
}
get shouldPreroll() {
if (CHROME) {
return false;
}
const {uURL, renamer} = this.download;
const {pathname, search, host} = uURL;
if (PREROLL_NOPE.has(host)) {
@ -167,39 +170,15 @@ export class Preroller {
rv.mime = type.essence;
}
const {p_ext: ext} = this.download.renamer;
const dispHeader = headers.get("content-disposition");
if (dispHeader) {
const file = CDPARSER.parse(dispHeader);
// Sanitize
rv.name = sanitizePath(file.replace(/[/\\]+/g, "-"));
}
else if (!ext || PREROLL_SEARCHEXTS.has(ext.toLocaleLowerCase())) {
const {searchParams} = this.download.uURL;
let detected = "";
for (const [, value] of searchParams) {
if (!NAME_TESTER.test(value)) {
continue;
}
const p = parsePath(value);
if (!p.base || !p.ext) {
continue;
}
if (!MimeDB.hasExtension(p.ext)) {
continue;
}
if (rv.mime) {
const mime = MimeDB.getMime(rv.mime);
if (mime && !mime.extensions.has(p.ext.toLowerCase())) {
continue;
}
}
const sanitized = sanitizePath(p.name);
if (sanitized.length <= detected.length) {
continue;
}
detected = sanitized;
}
else {
const detected = Preroller.maybeFindNameFromSearchParams(
this.download, rv);
if (detected) {
rv.name = detected;
}
@ -231,4 +210,43 @@ export class Preroller {
return rv;
}
static maybeFindNameFromSearchParams(
download: Download, res: PrerollResults) {
const {p_ext: ext} = download.renamer;
if (ext && !PREROLL_SEARCHEXTS.has(ext.toLocaleLowerCase())) {
return undefined;
}
return Preroller.findNameFromSearchParams(download.uURL, res.mime);
}
static findNameFromSearchParams(url: URL, mimetype?: string) {
const {searchParams} = url;
let detected = "";
for (const [, value] of searchParams) {
if (!NAME_TESTER.test(value)) {
continue;
}
const p = parsePath(value);
if (!p.base || !p.ext) {
continue;
}
if (!MimeDB.hasExtension(p.ext)) {
continue;
}
if (mimetype) {
const mime = MimeDB.getMime(mimetype);
if (mime && !mime.extensions.has(p.ext.toLowerCase())) {
continue;
}
}
const sanitized = sanitizePath(p.name);
if (sanitized.length <= detected.length) {
continue;
}
detected = sanitized;
}
return detected;
}
}

View File

@ -193,24 +193,24 @@ export default class Renamer {
}
toString() {
const {mask} = this.d;
const {mask, subfolder} = this.d;
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self: any = this;
// XXX flat
return sanitizePath(mask.replace(REPLACE_EXPR, function(type: string) {
const baseMask = subfolder ? `${subfolder}/${mask}` : mask;
return sanitizePath(baseMask.replace(REPLACE_EXPR, function(type: string) {
let prop = type.substr(1, type.length - 2);
const flat = prop.startsWith("flat");
if (flat) {
prop = prop.substr(4);
}
prop = `p_${prop}`;
const rv = (prop in self) ?
let rv = (prop in self) ?
(self[prop] || "").trim() :
type;
if (flat) {
return rv.replace(/[/\\]+/g, "-");
rv = rv.replace(/[/\\]+/g, "-");
}
return rv;
return rv.replace(/\/{2,}/g, "/");
}));
}
}

View File

@ -8,8 +8,9 @@ export const PAUSED = 1 << 3;
export const DONE = 1 << 4;
export const CANCELED = 1 << 5;
export const MISSING = 1 << 6;
export const RETRYING = 1 << 7;
export const RESUMABLE = PAUSED | CANCELED;
export const FORCABLE = PAUSED | QUEUED | CANCELED;
export const PAUSABLE = QUEUED | CANCELED | RUNNING;
export const CANCELABLE = QUEUED | RUNNING | PAUSED | DONE | MISSING;
export const RESUMABLE = PAUSED | CANCELED | RETRYING;
export const FORCABLE = PAUSED | QUEUED | CANCELED | RETRYING;
export const PAUSEABLE = QUEUED | CANCELED | RUNNING | RETRYING;
export const CANCELABLE = QUEUED | RUNNING | PAUSED | DONE | MISSING | RETRYING;

View File

@ -8,7 +8,7 @@ import {EventEmitter} from "./events";
const DEFAULTS = {
type: "basic",
iconUrl: extension.getURL("/style/icon64.png"),
title: "DownThemAll!",
title: "TraitorousDownloading!",
message: "message",
};
@ -25,7 +25,7 @@ export class Notification extends EventEmitter {
super();
this.generated = !id;
id = id || `DownThemAll-notification${++gid}`;
id = id || `TraitorousDownloading-notification${++gid}`;
if (typeof options === "string") {
options = {message: options};
}

View File

@ -99,6 +99,5 @@ export class PrefWatcher {
changed(prefs: any, key: string, value: any) {
this.value = value;
return true;
}
}

View File

@ -116,3 +116,14 @@ export const FASTFILTER = new RecentList("fastfilter", [
"*.z??, *.css, *.html"
]);
FASTFILTER.init().catch(console.error);
export const SUBFOLDER = new RecentList("subfolder", [
"",
"downthemall",
]);
SUBFOLDER.init().catch(console.error);
export const SERVER = new RecentList("server", [
"",
]);
SERVER.init().catch(console.error);

View File

@ -9,11 +9,12 @@ import { donate, openPrefs, openUrls } from "./windowutils";
// eslint-disable-next-line no-unused-vars
import { filters, FAST, Filter } from "./filters";
import { WindowStateTracker } from "./windowstatetracker";
import { windows } from "./browser";
import { windows, CHROME } from "./browser";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
interface BaseMatchedItem extends BaseItem {
sidx?: number;
matched?: string | null;
prevMatched?: string | null;
}
@ -28,7 +29,8 @@ function computeSelection(
items: BaseMatchedItem[],
onlyFast: boolean): ItemDelta[] {
let ws = items.map((item, idx: number) => {
item.idx = idx;
item.idx = item.idx || idx;
item.sidx = item.sidx || idx;
const {matched = null} = item;
item.prevMatched = matched;
item.matched = null;
@ -52,7 +54,7 @@ function computeSelection(
}
return items.filter(item => item.prevMatched !== item.matched).map(item => {
return {
idx: item.idx,
idx: item.sidx as number,
matched: item.matched
};
});
@ -98,10 +100,16 @@ export async function select(links: BaseItem[], media: BaseItem[]) {
type: "popup",
});
const window = await windows.create(windowOptions);
tracker.track(window.id, null);
tracker.track(window.id);
try {
if (!CHROME) {
windows.update(window.id, tracker.getOptions({}));
}
const port = await Promise.race<Port>([
new Promise<Port>(resolve => Bus.oncePort("select", resolve)),
new Promise<Port>(resolve => Bus.oncePort("select", port => {
resolve(port);
return true;
})),
timeout<Port>(5 * 1000)]);
if (!port.isSelf) {
throw Error("Invalid sender connected");

View File

@ -6,7 +6,7 @@ import { Bus, Port } from "./bus";
import { WindowStateTracker } from "./windowstatetracker";
import { Promised, timeout } from "./util";
import { donate } from "./windowutils";
import { windows } from "./browser";
import { windows, CHROME } from "./browser";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
@ -21,10 +21,16 @@ export async function single(item: BaseItem | null) {
type: "popup",
});
const window = await windows.create(windowOptions);
tracker.track(window.id, null);
tracker.track(window.id);
try {
if (!CHROME) {
windows.update(window.id, tracker.getOptions({}));
}
const port: Port = await Promise.race<Port>([
new Promise<Port>(resolve => Bus.oncePort("single", resolve)),
new Promise<Port>(resolve => Bus.oncePort("single", port => {
resolve(port);
return true;
})),
timeout<Port>(5 * 1000)]);
if (!port.isSelf) {
throw Error("Invalid sender connected");

View File

@ -3,6 +3,7 @@
import * as psl from "psl";
import { identity, memoize } from "./memoize";
import { IPReg } from "./ipreg";
export { debounce } from "../uikit/lib/util";
export class Promised {
@ -237,7 +238,10 @@ export interface URLd extends URL {
Object.defineProperty(URL.prototype, "domain", {
get() {
try {
return hostToDomain(this.host) || this.host;
const {hostname} = this;
return IPReg.test(hostname) ?
hostname :
hostToDomain(hostname) || hostname;
}
catch (ex) {
console.error(ex);
@ -357,3 +361,20 @@ export function mapFilterInSitu<TRes, T>(
export function randint(min: number, max: number) {
return Math.floor(Math.random() * (max - min)) + min;
}
export function validateSubFolder(folder: string) {
if (!folder) {
return;
}
folder = folder.replace(/[/\\]+/g, "/");
if (folder.startsWith("/")) {
throw new Error("error.noabsolutepath");
}
if (/^[a-z]:\//i.test(folder)) {
throw new Error("error.noabsolutepath");
}
if (/^\.+\/|\/\.+\/|\/\.+$/g.test(folder)) {
throw new Error("error.nodotsinpath");
}
}

View File

@ -3,6 +3,8 @@
import { Prefs } from "./prefs";
import { windows } from "./browser";
// eslint-disable-next-line no-unused-vars
import { Port } from "./bus";
const VALID_WINDOW_STATES = Object.freeze(new Set(["normal", "maximized"]));
@ -80,6 +82,7 @@ export class WindowStateTracker {
if (!this.windowId) {
return;
}
try {
const window = await windows.get(this.windowId);
if (!VALID_WINDOW_STATES.has(window.state)) {
return;
@ -97,17 +100,30 @@ export class WindowStateTracker {
}
await this.save();
}
catch {
// ignored
}
}
track(windowId: number, port: any) {
track(windowId: number, port?: Port) {
if (port) {
port.on("resized", this.update);
port.on("unload", e => this.finalize(e));
port.on("disconnect", this.finalize.bind(this));
}
this.windowId = windowId;
}
async finalize() {
async finalize(state?: any) {
if (state) {
this.left = state.left;
this.top = state.top;
}
await this.update();
this.windowId = 0;
if (state) {
await this.save();
}
}
async save() {

View File

@ -1,11 +1,20 @@
"use strict";
// License: MIT
import { windows, tabs, runtime } from "../lib/browser";
import { windows, tabs, runtime, CHROME } from "../lib/browser";
import { getManager } from "./manager/man";
import DEFAULT_ICONS from "../data/icons.json";
import { Prefs } from "./prefs";
import { _ } from "./i18n";
import { WindowStateTracker } from "./windowstatetracker";
// eslint-disable-next-line no-unused-vars
import { Port, Bus } from "./bus";
import { timeout } from "./util";
const DONATE_URL = "https://www.downthemall.org/howto/donate/";
const DONATE_LANG_URLS = Object.freeze(new Map([
["de", "https://www.downthemall.org/howto/donate/spenden/"],
]));
const MANAGER_URL = "/windows/manager.html";
export async function mostRecentBrowser(incognito: boolean): Promise<any> {
@ -21,7 +30,7 @@ export async function mostRecentBrowser(incognito: boolean): Promise<any> {
}
catch {
try {
window = await windows.getlastFocused();
window = await windows.getLastFocused();
if (window.type !== "normal") {
throw new Error("not a normal window");
}
@ -79,7 +88,8 @@ export async function maybeOpenInTab(url: string, incognito: boolean) {
}
export async function donate() {
await openInTab(DONATE_URL, false);
const url = DONATE_LANG_URLS.get(_("language_code")) || DONATE_URL;
await openInTab(url, false);
}
export async function openPrefs() {
@ -93,11 +103,59 @@ export async function openManager(focus = true) {
catch (ex) {
console.error(ex.toString(), ex);
}
const url = runtime.getURL(MANAGER_URL);
const openInPopup = await Prefs.get("manager-in-popup");
if (openInPopup) {
const etabs = await tabs.query({
url
});
if (etabs.length) {
if (!focus) {
return;
}
const tab = etabs.pop();
await tabs.update(tab.id, {active: true});
await windows.update(tab.windowId, {focused: true});
return;
}
const tracker = new WindowStateTracker("manager", {
minWidth: 700,
minHeight: 500,
});
await tracker.init();
const windowOptions = tracker.getOptions({
url,
type: "popup",
});
const window = await windows.create(windowOptions);
tracker.track(window.id);
try {
if (!CHROME) {
windows.update(window.id, tracker.getOptions({}));
}
const port = await Promise.race<Port>([
new Promise<Port>(resolve => Bus.oncePort("manager", port => {
resolve(port);
return true;
})),
timeout<Port>(5 * 1000)]);
if (!port.isSelf) {
throw Error("Invalid sender connected");
}
tracker.track(window.id, port);
}
catch (ex) {
console.error("couldn't track manager", ex);
}
return;
}
if (focus) {
await openInTabOrFocus(await runtime.getURL(MANAGER_URL), false);
await openInTabOrFocus(runtime.getURL(MANAGER_URL), false);
}
else {
await maybeOpenInTab(await runtime.getURL(MANAGER_URL), false);
await maybeOpenInTab(runtime.getURL(MANAGER_URL), false);
}
}

View File

@ -1,11 +1,11 @@
{
"manifest_version": 2,
"name": "DownThemAll!",
"version": "4.0.12",
"name": "TraitorousDownloading!",
"version": "4.2.6",
"description": "__MSG_extensionDescription__",
"homepage_url": "https://downthemall.org/",
"author": "Nils Maier",
"homepage_url": "https://github.com/lordwelch/downthemall",
"author": "lordwelch",
"default_locale": "en",
@ -16,7 +16,6 @@
"32": "style/icon32.png",
"48": "style/icon48.png",
"64": "style/icon64.png",
"96": "style/icon96.png",
"128": "style/icon128.png",
"256": "style/icon256.png"
},
@ -24,6 +23,7 @@
"permissions": [
"<all_urls>",
"contextMenus",
"cookies",
"downloads",
"downloads.open",
"downloads.shelf",
@ -33,6 +33,7 @@
"sessions",
"storage",
"tabs",
"theme",
"webNavigation",
"webRequest",
"webRequestBlocking"
@ -53,11 +54,10 @@
"32": "style/icon32.png",
"48": "style/icon48.png",
"64": "style/icon64.png",
"96": "style/icon96.png",
"128": "style/icon128.png",
"256": "style/icon256.png"
},
"default_title": "DownThemAll!"
"default_title": "TraitorousDownloading!"
},
"options_ui": {
@ -67,7 +67,7 @@
"browser_specific_settings": {
"gecko": {
"id": "dtalite@downthemall.org",
"id": "downloading@traitorousenterprises.net",
"strict_min_version": "67.0"
}
}

View File

@ -1,13 +1,14 @@
{
"name": "dtalite",
"name": "tdl",
"version": "4.0.0",
"description": "DownThemAll! lite",
"description": "TraitorousDownloading!",
"main": "main.js",
"directories": {
"lib": "lib"
},
"scripts": {
"build": "util/build.py",
"build:cleanup": "rm -rf bundles",
"build:bundles": "webpack",
"build:regexps": "node util/makexregexps.js > data/xregexps.json",
"stats": "cloc --vcs=git --exclude-lang=Markdown,SVG",
@ -18,24 +19,26 @@
"author": "Nils Maier",
"license": "MIT",
"devDependencies": {
"@types/node": "^12.7.2",
"@typescript-eslint/eslint-plugin": "^2.0.0",
"@typescript-eslint/parser": "^2.0.0",
"@types/node": "^12.7.8",
"@typescript-eslint/eslint-plugin": "^2.3.2",
"@typescript-eslint/parser": "^2.3.2",
"chai": "^4.1.2",
"eslint": "^6.2.2",
"mocha": "^6.2.0",
"ts-loader": "^6.0.4",
"ts-node": "^8.3.0",
"typescript": "^3.5.3",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"eslint": "^6.5.1",
"mocha": "^6.2.1",
"ts-loader": "^6.2.0",
"ts-node": "^8.4.1",
"typescript": "^3.6.3",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",
"xregexp": "^4.2.4"
},
"dependencies": {
"@types/psl": "^1.1.0",
"@types/whatwg-mimetype": "^2.1.0",
"psl": "^1.3.0",
"webextension-polyfill": "^0.4.0",
"localforage": "^1.9.0",
"psl": "^1.4.0",
"webextension-polyfill": "^0.5.0",
"webextension-polyfill-ts": "^0.22.0",
"whatwg-mimetype": "^2.3.0"
}
}

View File

@ -121,14 +121,26 @@ class Gatherer {
*collectImageInternal(img: HTMLImageElement) {
try {
const src = img.currentSrc || img.src;
{
const {src} = img;
const item = this.makeItem(src, img);
if (item) {
item.fileName = "";
item.description = item.title;
yield item;
}
}
{
const {currentSrc} = img;
const item = this.makeItem(currentSrc, img);
if (item) {
item.fileName = "";
item.description = item.title;
yield item;
}
}
{
const {srcset} = img;
if (!srcset) {
return;
@ -146,6 +158,7 @@ class Gatherer {
}
}
}
}
catch (ex) {
console.error("oops image", ex.toString(), ex.stack, ex);
}

BIN
sounds/done.wav Normal file

Binary file not shown.

BIN
sounds/error.wav Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 B

After

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 725 B

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -2,14 +2,29 @@
/* License: gpl-v2 */
:root {
--general-color: #2a2a2e;
--general-bgcolor: rgb(249, 249, 250);
--general-border-color: lightgray;
--general-input-color: black;
--general-input-bgcolor: white;
--general-button-color: black;
--general-button-bgcolor: rgb(246, 246, 246);
--general-button-bgcolor-hover: white;
--general-button-shadow: 0px 0px 5px 1px rgba(128, 128, 128, 0.5);
--menu-bgcolor: white;
--menu-bgcolor-hover: #2283fb;
--table-bgcolor: white;
--table-head-bgcolor: white;
--toolbar-bg-color: rgb(248, 134, 6);
--toolbar-active-border-color: #478de7;
--toolbar-hover-border-color: red;
--toolbar-hover-background: rgb(247, 149, 37);
--toolbar-border-width: 2px;
--toolbar-border: 1px solid rgba(255, 255, 255, 0.5);
--add-color: navy;
--queue-color: gray;
--pause-color: #ffa318;
--retry-color: rgb(0, 112, 204);
--error-color: rgb(160, 13, 42);
--running-color: #aae061;
--finishing-color: #57cc12;
@ -19,24 +34,81 @@
--maskbutton-color: rgb(236, 185, 16);
--missing-color: rgb(0, 82, 204);
--open-color: rgba(236, 185, 16, 0.8);
--status-icon-color: #363636;
--status-icon-color-hover: #6e6d6d;
--tile-url: url(tile.png);
--file-icon-image-color: rgb(17, 107, 163);
--popup-bgcolor: #fff;
--popup-color: #0c0c0d;
--modal-color: black;
--modal-bgcolor: white;
}
html.dark {
--add-color: lightblue;
--error-color: rgb(130, 3, 22);
--running-color: #67a041;
--finishing-color: #4bb111;
--done-color: #006f00;
--pause-color: #cf9308;
--general-bgcolor: #2a2a2e;
--general-border-color: rgb(85, 85, 85);
--general-button-bgcolor-hover: black;
--general-button-bgcolor: rgb(36, 36, 36);
--general-button-color: white;
--general-color: rgb(249, 249, 250);
--menu-bgcolor: black;
--menu-bgcolor-hover: #1a6bce;
--table-bgcolor: #1a1a1e;
--table-head-bgcolor: #3a3a3e;
--toolbar-bg-color: rgb(202, 108, 0);
--status-icon-color: #b9b9b9;
--status-icon-color-hover: #e2e2e2;
--tile-url: url(tile-dark.png?3);
--toolbar-border: 1px solid rgba(30, 30, 30, 0.5);
--file-icon-image-color: rgb(21, 130, 197);
--popup-bgcolor: #4a4a4f;
--popup-color: rgb(249, 249, 250);
--general-button-shadow: 0px 0px 7px 1px rgba(128, 128, 128, 0.8);
--modal-color: white;
--modal-bgcolor: #333;
scrollbar-color: rgba(249, 249, 250, 0.4) rgba(20, 20, 25, 0.3);
}
html.dark a {
color: lightblue;
}
html.dark ::-webkit-scrollbar {
background: rgba(20, 20, 25, 0.3);
}
html.dark ::-webkit-scrollbar-thumb {
background: rgba(249, 249, 250, 0.4);
}
html.dark ::-webkit-scrollbar-corner {
background: #000;
}
html[data-platform="mac"] {
--folder-color: rgb(4, 102, 214);
}
html, body {
html,
body {
font-size: 10pt !important;
}
@font-face {
font-family: 'downthemall';
src: url('downthemall.woff2?75791791') format('woff2');
font-family: "downthemall";
src: url("downthemall.woff2?75791791") format("woff2");
font-weight: normal;
font-style: normal;
}
[class^="icon-"]:before, [class*=" icon-"]:before {
[class^="icon-"]:before,
[class*=" icon-"]:before {
font-family: "downthemall";
font-style: normal;
font-weight: normal;
@ -53,70 +125,187 @@ html, body {
-moz-osx-font-smoothing: grayscale;
}
.icon-add:before { content: '\e800'; } /* '' */
.icon-addsegment:before { content: '\e801'; } /* '' */
.icon-bottom:before { content: '\e802'; } /* '' */
.icon-picture:before { content: '\e803'; } /* '' */
.icon-circle:before { content: '\e804'; } /* '' */
.icon-delete:before { content: '\e805'; } /* '' */
.icon-done:before { content: '\e806'; } /* '' */
.icon-down:before { content: '\e807'; } /* '' */
.icon-download:before { content: '\e808'; } /* '' */
.icon-dupe:before { content: '\e809'; } /* '' */
.icon-error:before { content: '\e80a'; } /* '' */
.icon-failed:before { content: '\e80b'; } /* '' */
.icon-file:before { content: '\e80c'; } /* '' */
.icon-find:before { content: '\e80d'; } /* '' */
.icon-folder:before { content: '\e80e'; } /* '' */
.icon-force:before { content: '\e80f'; } /* '' */
.icon-go:before { content: '\e810'; } /* '' */
.icon-import:before { content: '\e811'; } /* '' */
.icon-info:before { content: '\e812'; } /* '' */
.icon-launch:before { content: '\e813'; } /* '' */
.icon-missing:before { content: '\e814'; } /* '' */
.icon-network-off:before { content: '\e815'; } /* '' */
.icon-network-on:before { content: '\e816'; } /* '' */
.icon-pause:before { content: '\e817'; } /* '' */
.icon-remsegment:before { content: '\e818'; } /* '' */
.icon-rename:before { content: '\e819'; } /* '' */
.icon-save:before { content: '\e81a'; } /* '' */
.icon-settings:before { content: '\e81b'; } /* '' */
.icon-top:before { content: '\e81c'; } /* '' */
.icon-unchecked:before { content: '\e81d'; } /* '' */
.icon-unlimited:before { content: '\e81e'; } /* '' */
.icon-link:before { content: '\e81f'; } /* '' */
.icon-up:before { content: '\e820'; } /* '' */
.icon-privacy:before { content: '\e821'; } /* '' */
.icon-tags:before { content: '\e822'; } /* '' */
.icon-attention:before { content: '\e823'; } /* '' */
.icon-notification:before { content: '\e824'; } /* '' */
.icon-file-video:before { content: '\e825'; } /* '' */
.icon-file-generic:before { content: '\e826'; } /* '' */
.icon-question-dark:before { content: '\e827'; } /* '' */
.icon-filter:before { content: '\f0b0'; } /* '' */
.icon-donate:before { content: '\f0d6'; } /* '' */
.icon-file-doc:before { content: '\f0f6'; } /* '' */
.icon-interface:before { content: '\f108'; } /* '' */
.icon-folder-1:before { content: '\f115'; } /* '' */
.icon-sort-asc:before { content: '\f15d'; } /* '' */
.icon-sort-desc:before { content: '\f15e'; } /* '' */
.icon-file-pdf:before { content: '\f1c1'; } /* '' */
.icon-file-word:before { content: '\f1c2'; } /* '' */
.icon-file-image:before { content: '\f1c5'; } /* '' */
.icon-file-archive:before { content: '\f1c6'; } /* '' */
.icon-file-audio:before { content: '\f1c7'; } /* '' */
.icon-toggle:before { content: '\f205'; } /* '' */
.icon-server:before { content: '\f233'; } /* '' */
.icon-question-light:before { content: '\f29c'; } /* '' */
.icon-add:before {
content: "\e800";
} /* '' */
.icon-addsegment:before {
content: "\e801";
} /* '' */
.icon-bottom:before {
content: "\e802";
} /* '' */
.icon-picture:before {
content: "\e803";
} /* '' */
.icon-circle:before {
content: "\e804";
} /* '' */
.icon-delete:before {
content: "\e805";
} /* '' */
.icon-done:before {
content: "\e806";
} /* '' */
.icon-down:before {
content: "\e807";
} /* '' */
.icon-download:before {
content: "\e808";
} /* '' */
.icon-dupe:before {
content: "\e809";
} /* '' */
.icon-error:before {
content: "\e80a";
} /* '' */
.icon-failed:before {
content: "\e80b";
} /* '' */
.icon-file:before {
content: "\e80c";
} /* '' */
.icon-find:before {
content: "\e80d";
} /* '' */
.icon-folder:before {
content: "\e80e";
} /* '' */
.icon-force:before {
content: "\e80f";
} /* '' */
.icon-go:before {
content: "\e810";
} /* '' */
.icon-import:before {
content: "\e811";
} /* '' */
.icon-info:before {
content: "\e812";
} /* '' */
.icon-launch:before {
content: "\e813";
} /* '' */
.icon-missing:before {
content: "\e814";
} /* '' */
.icon-network-off:before {
content: "\e815";
} /* '' */
.icon-network-on:before {
content: "\e816";
} /* '' */
.icon-pause:before {
content: "\e817";
} /* '' */
.icon-remsegment:before {
content: "\e818";
} /* '' */
.icon-rename:before {
content: "\e819";
} /* '' */
.icon-save:before {
content: "\e81a";
} /* '' */
.icon-settings:before {
content: "\e81b";
} /* '' */
.icon-top:before {
content: "\e81c";
} /* '' */
.icon-unchecked:before {
content: "\e81d";
} /* '' */
.icon-unlimited:before {
content: "\e81e";
} /* '' */
.icon-link:before {
content: "\e81f";
} /* '' */
.icon-up:before {
content: "\e820";
} /* '' */
.icon-privacy:before {
content: "\e821";
} /* '' */
.icon-tags:before {
content: "\e822";
} /* '' */
.icon-attention:before {
content: "\e823";
} /* '' */
.icon-notification:before {
content: "\e824";
} /* '' */
.icon-file-video:before {
content: "\e825";
} /* '' */
.icon-file-generic:before {
content: "\e826";
} /* '' */
.icon-question-dark:before {
content: "\e827";
} /* '' */
.icon-forward:before {
content: "\e828";
} /* '' */
.icon-filter:before {
content: "\f0b0";
} /* '' */
.icon-donate:before {
content: "\f0d6";
} /* '' */
.icon-file-doc:before {
content: "\f0f6";
} /* '' */
.icon-interface:before {
content: "\f108";
} /* '' */
.icon-folder-1:before {
content: "\f115";
} /* '' */
.icon-sort-asc:before {
content: "\f15d";
} /* '' */
.icon-sort-desc:before {
content: "\f15e";
} /* '' */
.icon-file-pdf:before {
content: "\f1c1";
} /* '' */
.icon-file-word:before {
content: "\f1c2";
} /* '' */
.icon-file-image:before {
content: "\f1c5";
} /* '' */
.icon-file-archive:before {
content: "\f1c6";
} /* '' */
.icon-file-audio:before {
content: "\f1c7";
} /* '' */
.icon-toggle-off:before {
content: "\f204";
} /* '' */
.icon-toggle-on:before {
content: "\f205";
} /* '' */
.icon-server:before {
content: "\f233";
} /* '' */
.icon-question-light:before {
content: "\f29c";
} /* '' */
@media (min-resolution: 144dpi) {
[class^="icon-file-"]:before, [class*=" icon-file-"]:before {
[class^="icon-file-"]:before,
[class*=" icon-file-"]:before {
font-weight: bold !important;
}
}
.icon-file-image {
color: rgb(17, 107, 163);
color: var(--file-icon-image-color);
}
.icon-file-pdf,
@ -137,18 +326,29 @@ html, body {
color: rgb(202, 81, 198);
}
body, html {
background: #F6F6F8;
color: #0C0C0D;
body,
html {
font: message-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Ubuntu",
"Helvetica Neue", sans-serif;
height: 100%;
margin: 0;
padding: 0;
width: 100%;
color: var(--general-color);
background: var(--general-bgcolor);
}
h1, h2, h3, h4 {
html#popup,
html#popup > body {
color: var(--popup-color);
background: var(--popup-bgcolor);
}
h1,
h2,
h3,
h4 {
font: caption;
font-weight: bold;
}
@ -188,7 +388,11 @@ section {
}
.virtualtable-column:active {
background-image: linear-gradient(to top, rgba(0,0,0,0.03), rgba(128,128,128,0.1));
background-image: linear-gradient(
to top,
rgba(0, 0, 0, 0.03),
rgba(128, 128, 128, 0.1)
);
}
th.virtualtable {
@ -215,7 +419,11 @@ td.virtualtable {
font-size: 12px;
align-items: stretch;
justify-items: center;
background: linear-gradient(to bottom, rgba(128,128,128,0.1) 0%,rgba(0,0,0,0) 100%);
background: linear-gradient(
to bottom,
rgba(128, 128, 128, 0.1) 0%,
rgba(0, 0, 0, 0) 100%
);
border-top: 1px solid rgba(128, 128, 128, 0.6);
display: flex;
margin-bottom: 1ex;
@ -248,10 +456,10 @@ td.virtualtable {
#statusPrefs {
cursor: pointer;
color: #363636;
color: var(--status-icon-color);
}
#statusPrefs:hover {
color: #6e6d6d;
color: var(--status-icon-color-hover);
}
.dropdown {
@ -276,6 +484,7 @@ td.virtualtable {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
color: black;
background: white;
border: none;
bottom: 2px;
@ -298,7 +507,6 @@ td.virtualtable {
padding-bottom: 1ex;
}
@supports (not (-moz-appearance: none)) {
.dropdown select {
background: white;
@ -368,3 +576,54 @@ td.virtualtable {
#maskButton {
color: var(--maskbutton-color);
}
table.virtualtable,
.virtualtable-body {
color: var(--general-color);
background: var(--table-bgcolor);
}
.virtualtable-head,
.virtualtable-head > table {
background: var(--table-head-bgcolor) !important;
}
.virtualtable-column {
border-right: 1px solid var(--general-border-color);
}
.virtualtable-cell {
border-right: 1px dotted var(--general-border-color);
}
.virtualtable-head,
.virtualtable-body {
border-bottom: 1px solid var(--general-border-color);
}
ul.context-menu,
ul.context-menu ul {
color: var(--general-color);
background: var(--menu-bgcolor);
}
.context-menu-item:hover:not(.context-menu-seperator),
.context-menu-item:hover:not(.context-menu-seperator) > * {
background: var(--menu-bgcolor-hover);
}
html.dark .context-menu-item.disabled,
html.dark .context-menu-item.disabled > * {
opacity: 0.7;
}
input {
color: var(--general-input-color);
background: var(--general-input-bgcolor);
border: inherit;
padding: 2px;
}
.modal-dialog {
color: var(--modal-color);
background: var(--modal-bgcolor);
}

BIN
style/done.opus Normal file

Binary file not shown.

BIN
style/downthemall.woff2 Normal file → Executable file

Binary file not shown.

BIN
style/error.opus Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -16,7 +16,7 @@ body > * {
#toolbar {
display: flex;
margin: 0;
background: var(--toolbar-bg-color) url(tile.png) repeat-x;
background: var(--toolbar-bg-color) var(--tile-url) repeat-x;
}
#toolbar .spacer {
@ -42,9 +42,9 @@ body > * {
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0px 0px 5px 1px rgba(128,128,128,0.5);
background: rgb(246,246,246);
color: black;
box-shadow: var(--general-button-shadow);
background: var(--general-button-bgcolor);
color: var(--general-button-color);
transition: box-shadow 0.5s, background 1s;
font-size: 24px;
line-height: 24px;
@ -60,7 +60,7 @@ body > * {
}
#toolbar > .button:hover:not(.disabled) {
background: white;
background: var(--general-button-bgcolor-hover);
box-shadow: 0px 0px 7px 2px rgba(70,70,70,0.75);
}
@ -88,14 +88,14 @@ body > * {
flex: 1 1 auto;
overflow: hidden;
display: grid;
background: white;
background: var(--general-bgcolor);
}
#loading {
display: flex;
justify-content: center;
align-items: center;
background: rgba(255,255,255,0.9);
background: var(--general-button-bgcolor);
font-weight: bolder;
font-size: 200%;
z-index: 10;
@ -202,6 +202,23 @@ body > * {
);
}
.retrying .virtualtable-column-2 .virtualtable-icon {
color: var(--retry-color);
}
.retrying .virtualtable-column-2 .virtualtable-progress-bar {
background: var(--retry-color);
}
.retrying .virtualtable-column-2 .virtualtable-progress-undetermined {
background: repeating-linear-gradient(
45deg,
var(--retry-color),
var(--retry-color) 6px,
transparent 6px,
transparent 12px
);
}
.missing .virtualtable-column-2 .virtualtable-icon,
.canceled .virtualtable-column-2 .virtualtable-icon {
color: var(--error-color);
@ -294,7 +311,7 @@ body > * {
color: crimson;
}
#statusNetwork.icon-network-on {
color: navy;
color: var(--add-color);
}
#statusFilter {
@ -327,6 +344,7 @@ body > * {
height: 16px;
-moz-appearance: none;
border: 0;
outline: 0;
background: transparent;
width: calc(100% - 28px);
}
@ -386,7 +404,7 @@ body > * {
font-size: 10pt !important;
}
#nagging {
border-top: 1px solid lightgray;
border-top: 1px solid var(--general-border-color);
display: grid;
grid-template-columns: 1fr auto auto auto;
align-content: center;
@ -512,3 +530,23 @@ body > * {
width: 100%;
background: var(--done-color);
}
#tooltip-eta.single {
font-weight: bold;
grid-column-end: span 2;
}
.deletefiles-list {
padding-left: 1ex;
padding-right: 1.5ex;
border: 1px solid lightgray;
border-radius: 6px;
background-color: rgba(128,128,128,0.1);
max-height: 8em;
overflow-y: auto;
}
.deletefiles-list > li {
list-style-type: none;
padding: 0;
margin: 0;
}

View File

@ -1,8 +1,8 @@
/* License: gpl-v2 */
@import 'common.css';
@import "common.css";
html, body {
background: transparent !important;
html,
body {
width: 100%;
height: 100%;
}
@ -19,9 +19,10 @@ article {
#tabs {
display: flex;
background: url(icon64.png) 1em 50%/32px 32px no-repeat, url(tile.png) repeat-x, var(--toolbar-bg-color);
background: url(icon64.png) 1em 50%/32px 32px no-repeat,
var(--tile-url) repeat-x, var(--toolbar-bg-color);
padding-left: calc(2em + 32px);
color: white;
color: var(--general-bgcolor);
}
input.tab {
@ -54,9 +55,10 @@ input.tab {
#tabsel-general:checked ~ #tabs #tabel-general,
#tabsel-filters:checked ~ #tabs #tabel-filters,
#tabsel-network:checked ~ #tabs #tabel-network {
color: black !important;
background: white;
border-top: var(--toolbar-border-width) solid var(--toolbar-active-border-color);
color: var(--general-color) !important;
background: var(--general-bgcolor);
border-top: var(--toolbar-border-width) solid
var(--toolbar-active-border-color);
}
#tabs > label {
@ -64,13 +66,14 @@ input.tab {
border-top: var(--toolbar-border-width) solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-left: 1px solid rgba(255, 255, 255, 0.5);
border-right: 1px solid rgba(255, 255, 255, 0.5);
border-left: var(--toolbar-border);
border-right: var(--toolbar-border);
background: var(--toolbar-bg-color);
}
#tabs > label:hover:not(:checked) {
border-top: var(--toolbar-border-width) solid var(--toolbar-hover-border-color);
border-top: var(--toolbar-border-width) solid
var(--toolbar-hover-border-color);
background: var(--toolbar-hover-background);
}
@ -113,15 +116,27 @@ input.tab {
fieldset {
display: flex;
margin-bottom: 1em;
border: 1px solid lightgray;
border: 1px solid var(--general-border-color);
border-radius: 6px;
box-shadow: 1px 1px 6px lightgray;
box-shadow: 1px 1px 6px var(--general-border-color);
background: rgba(128, 128, 128, 0.05);
flex-direction: column;
max-width: 60em;
padding: 1.2em;
}
.optiongroups,
fieldset > label {
display: flex;
align-items: center;
}
fieldset > label > input,
fieldset > label > select {
margin-left: 1ex;
margin-right: 1ex;
}
legend {
font-weight: bold;
font-size: 120%;
@ -134,8 +149,36 @@ legend {
}
.virtualtable-container {
border: 1px solid lightgray;
border: 1px solid var(--general-border-color);
border-radius: 6px;
background: rgba(128, 128, 128, 0.05);
box-shadow: 1px 1px 6px lightgray;
box-shadow: 1px 1px 6px var(--general-border-color);
}
#network-general {
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 1em;
grid-row-gap: 1ex;
}
.optiongroups {
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 1em;
grid-row-gap: 1ex;
}
.optiongroups > div,
.optiongroups > div > label {
display: flex;
align-items: center;
}
.optiongroups input {
margin-left: 1em;
margin-right: 0.7ex;
}
hr {
width: 100%;
}

View File

@ -107,7 +107,7 @@ body > * {
padding: 0;
padding-left: calc(2em + 32px);
color: black;
background: url(icon32.png) 1em 0/32px 32px no-repeat, url(tile.png) repeat-x, var(--toolbar-bg-color);
background: url(icon32.png) 1em 0/32px 32px no-repeat, var(--tile-url) repeat-x, var(--toolbar-bg-color);
font: caption;
font-size: 150%;
font-weight: bold;
@ -117,7 +117,7 @@ body > * {
}
@media (-webkit-min-device-pixel-ratio: 1.3), (min-resolution: 124.8dpi) {
#tabs {
background: url(icon64.png) 1em 50%/32px 32px no-repeat, url(tile.png) repeat-x, var(--toolbar-bg-color);
background: url(icon64.png) 1em 50%/32px 32px no-repeat, var(--tile-url) repeat-x, var(--toolbar-bg-color);
}
}
@ -145,27 +145,27 @@ body > * {
-webkit-user-select: none;
user-select: none;
background: var(--toolbar-bg-color);
color: white;
color: var(--general-color);
min-width: 10em;
padding: 1ex;
padding-left: 1em;
cursor: pointer;
border: 0;
border-top: var(--toolbar-border-width) solid transparent;
border-left: 1px solid rgba(255,255,255,0.3);
border-right: 1px solid rgba(255,255,255,0.3);
border-left: var(--toolbar-border);
border-right: var(--toolbar-border);
transition: border 1s;
}
.tab:not(.active):not(.disabled):hover {
border-top: var(--toolbar-border-width) solid var(--toolbar-hover-border-color);
color: rgb(255, 226, 167);
color: var(--general-color);
background: var(--toolbar-hover-background);
}
.tab.active {
color: black;
background: white;
color: var(--general-color);
background: var(--table-head-bgcolor);
border-top: var(--toolbar-border-width) solid var(--toolbar-active-border-color);
border-left: 1px solid transparent;
border-right: 1px solid transparent;

View File

@ -63,7 +63,8 @@ p.example {
align-items: center;
}
#options > #maskOptions {
#options > #subfolderOptions,
#options > #maskOptions, #options > #serverOptions {
display: grid;
grid-template-columns: 2fr auto auto;
}

BIN
style/tile-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

10
tests/.editorconfig Normal file
View File

@ -0,0 +1,10 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
tab_width = 2
trim_trailing_whitespace = true

View File

@ -113,4 +113,29 @@ describe("BatchGenerator", function() {
expect(items[0]).to.equal(gen.preview);
expect(gen.hasInvalid).to.be.true;
});
it("characters", function() {
const gen = new BatchGenerator("abc[a:c].lol[1].b");
const items = Array.from(gen);
expect(items).to.deep.equal([
"abca.lol[1].b",
"abcb.lol[1].b",
"abcc.lol[1].b",
]);
expect(items.length).to.equal(gen.length);
expect(items[0]).to.equal(gen.preview);
});
it("characters two", function() {
const gen = new BatchGenerator("abc[D:G].lol[1].b");
const items = Array.from(gen);
expect(items).to.deep.equal([
"abcD.lol[1].b",
"abcE.lol[1].b",
"abcF.lol[1].b",
"abcG.lol[1].b",
]);
expect(items.length).to.equal(gen.length);
expect(items[0]).to.equal(gen.preview);
});
});

43
tests/test_urld.js Normal file
View File

@ -0,0 +1,43 @@
/* eslint-env node */
/* eslint-disable @typescript-eslint/no-var-requires */
"use strict";
// License: CC0 1.0
require("../lib/util");
describe("URLd", function() {
it("basic domain", function() {
let u = new URL("https://www.google.de");
expect(u.domain).to.equal("google.de");
u = new URL("https://www.google.de:8443");
expect(u.domain).to.equal("google.de");
});
it("plain basic domain", function() {
const u = new URL("https://google.de");
expect(u.domain).to.equal("google.de");
});
it("special domain", function() {
let u = new URL("https://www.google.co.uk");
expect(u.domain).to.equal("google.co.uk");
u = new URL("https://google.co.uk");
expect(u.domain).to.equal("google.co.uk");
u = new URL("https://www.google.co.uk:8443");
expect(u.domain).to.equal("google.co.uk");
});
it("ipv4", function() {
let u = new URL("https://127.0.0.1:8443");
expect(u.domain).to.equal("127.0.0.1");
u = new URL("https://0.0.0.0:8443");
expect(u.domain).to.equal("0.0.0.0");
});
it("ipv6", function() {
let u = new URL("https://[::1]:8443");
expect(u.domain).to.equal("[::1]");
u = new URL("https://[2a00:1450:4005:800::2003]:8443");
expect(u.domain).to.equal("[2a00:1450:4005:800::2003]");
});
});

View File

@ -108,8 +108,13 @@ export class MenuItem extends MenuItemBase {
super(owner, id, text, options);
this.disabled = options.disabled === "true";
this.elem.setAttribute("aria-role", "menuitem");
this.elem.addEventListener(
"click", () => this.owner.emit("clicked", this.id, this.autoHide));
this.clicked = this.clicked.bind(this);
this.elem.addEventListener("click", this.clicked);
this.elem.addEventListener("contextmenu", this.clicked);
}
clicked() {
this.owner.emit("clicked", this.id, this.autoHide);
}
get disabled() {

View File

@ -104,7 +104,7 @@ export class EventEmitter {
for (const e of Array.from(handlers)) {
try {
// eslint-disable-next-line prefer-spread
handled = handled || !!e.apply(null, args);
handled = !!e.apply(null, args) || handled;
}
catch (ex) {
console.error(`Event handler ${e} for ${event} failed`, ex.toString(), ex.stack, ex);

View File

@ -45,10 +45,10 @@ export class TableEvents extends BaseTable {
"scroll", debounce(this.scrolled.bind(this), SCROLL_DEBOUNCE), {
passive: true
});
body.addEventListener("contextmenu", this.contextmenu.bind(this), true);
table.addEventListener("keypress", this.keypressed.bind(this), true);
table.addEventListener("keydown", this.keypressed.bind(this), true);
table.addEventListener("contextmenu", this.contextmenu.bind(this), true);
selectionGrippy.addEventListener("click", this.grippyClicked.bind(this));
}

View File

@ -20,16 +20,19 @@ FILES = [
"LICENSE.*",
]
RELEASE_ID = "{DDC359D1-844A-42a7-9AA1-88A850A938A8}"
RELEASE_ID = "downloading@traitorousenterprises.net"
UNCOMPRESSABLE = set((".png", ".jpg", ".zip", ".woff2"))
LICENSED = set((".css", ".html", ".js", "*.ts"))
IGNORED = set((".DS_Store", "Thumbs.db"))
# XXX: #125
IGNORED_OPERA = set(("done.opus", "error.opus"))
PERM_IGNORED_FX = set(("downloads.shelf", "webRequest", "webRequestBlocking"))
PERM_IGNORED_CHROME = set(("menus", "sessions"))
PERM_IGNORED_CHROME = set(("menus", "sessions", "theme"))
SCRIPTS = [
"yarn build:cleanup",
"yarn build:regexps",
"yarn build:bundles",
]
@ -45,17 +48,17 @@ def check_licenses():
raise Exception(f"No license in {file}")
def files():
def files(additional_ignored):
p = Path("")
for pattern in FILES:
for file in sorted(p.glob(pattern)):
if file.name in IGNORED or not file.is_file():
if file.name in IGNORED or file.name in additional_ignored or not file.is_file():
continue
yield file
def build(out, manifest):
with ZipFile(out, "w", compression=ZIP_DEFLATED, allowZip64=False, compresslevel=2) as zp:
for file in files():
def build(out, manifest, additional_ignored=set()):
with ZipFile(out, "w", compression=ZIP_DEFLATED, allowZip64=False) as zp:
for file in files(additional_ignored):
if str(file) == "manifest.json":
buf = manifest
else:
@ -68,7 +71,7 @@ def build(out, manifest):
if file.suffix in UNCOMPRESSABLE:
zp.writestr(zinfo, buf, compress_type=ZIP_STORED)
else:
zp.writestr(zinfo, buf, compress_type=ZIP_DEFLATED, compresslevel=2)
zp.writestr(zinfo, buf, compress_type=ZIP_DEFLATED)
print(file)
@ -85,14 +88,14 @@ def build_firefox(args):
if args.mode != "release":
infos["version_name"] = f"{version}-{args.mode}"
infos["browser_specific_settings"]["gecko"]["id"] = f"{args.mode}@downthemall.org"
infos["browser_specific_settings"]["gecko"]["id"] = f"{args.mode}@traitorousenterprises.net"
infos["short_name"] = infos.get("name")
infos["name"] = f"{infos.get('name')} {args.mode}"
else:
infos["browser_specific_settings"]["gecko"]["id"] = RELEASE_ID
infos["permissions"] = [p for p in infos.get("permissions") if not p in PERM_IGNORED_FX]
out = Path("web-ext-artifacts") / f"dta-{version}-{args.mode}-fx.zip"
out = Path("web-ext-artifacts") / f"tdl-{version}-{args.mode}-fx.zip"
if not out.parent.exists():
out.parent.mkdir()
if out.exists():
@ -101,7 +104,7 @@ def build_firefox(args):
build(out, json.dumps(infos, indent=2).encode("utf-8"))
def build_chrome(args):
def build_chromium(args, pkg, additional_ignored=set()):
now = datetime.now().strftime("%Y%m%d%H%M%S")
with open("manifest.json") as manip:
infos = json.load(manip, object_pairs_hook=OrderedDict)
@ -119,13 +122,13 @@ def build_chrome(args):
infos["name"] = f"{infos.get('name')} {args.mode}"
infos["permissions"] = [p for p in infos.get("permissions") if not p in PERM_IGNORED_CHROME]
out = Path("web-ext-artifacts") / f"dta-{version}-{args.mode}-crx.zip"
out = Path("web-ext-artifacts") / f"tdl-{version}-{args.mode}-{pkg}.zip"
if not out.parent.exists():
out.parent.mkdir()
if out.exists():
out.unlink()
print("Output", out)
build(out, json.dumps(infos, indent=2).encode("utf-8"))
build(out, json.dumps(infos, indent=2).encode("utf-8"), additional_ignored=additional_ignored)
def main():
from argparse import ArgumentParser
@ -140,7 +143,8 @@ def main():
else:
run([script], shell=True)
build_firefox(args)
build_chrome(args)
build_chromium(args, "crx")
build_chromium(args, "opr", IGNORED_OPERA)
print("DONE.")
if __name__ == "__main__":

View File

@ -6,7 +6,7 @@ langs = sorted(Path("_locales").glob("**/messages.json"), key=lambda p: p.parent
all = {}
for m in langs:
loc = m.parent.name
with m.open("r") as mp:
with m.open("r", encoding="utf-8") as mp:
lang = json.load(mp).get("language").get("message")
if not lang:
raise Exception(f"{m}: no language")

View File

@ -22,6 +22,9 @@ export class Dropdown extends EventEmitter {
this.container = document.createElement("div");
this.container.classList.add("dropdown");
if (input.id) {
this.container.id = `${input.id}-dropdown`;
}
input = input.parentElement.replaceChild(this.container, input);
this.input = input as HTMLInputElement;

View File

@ -3,7 +3,7 @@
<!-- License: gpl-v2 -->
<head>
<meta charset="utf-8">
<title data-i18n="manager.title">DownThemAll!</title>
<title data-i18n="manager.title">TraitorousDownloading!</title>
<style>
@import "/uikit/css/virtualtable.css";
@import "/uikit/css/modal.css";
@ -13,8 +13,8 @@
<link rel="icon" href="/style/icon32.png">
<link rel="icon" sizes="16x16" href="/style/icon16.png">
<link rel="icon" sizes="32x32" href="/style/icon32.png">
<link rel="icon" sizes="48x48" href="/style/icon48.png">
<link rel="icon" sizes="64x64" href="/style/icon64.png">
<link rel="icon" sizes="96x96" href="/style/icon96.png">
<link rel="icon" sizes="128x128" href="/style/icon128.png">
<link rel="icon" sizes="256x256" href="/style/icon256.png">
<script defer src="/bundles/common.js"></script>
@ -75,6 +75,7 @@
<ul id="table-context" class="table-context">
<li id="ctx-open-file" data-key="Enter" data-i18n="open-file" data-icon="icon-file">Open File</li>
<li id="ctx-open-directory" data-key="ACCEL-Enter" data-i18n="open-directory" data-icon="icon-folder">Open Directory</li>
<li id="ctx-delete-files" data-key="ACCEL-Delete" data-i18n="deletefiles" data-icon="icon-delete"></li>
<li>-</li>
<li id="ctx-resume" data-key="ACCEL-KeyR" data-i18n="resume-download" data-icon="icon-go">Resume</li>
<li id="ctx-pause" data-key="ACCEL-KeyP" data-i18n="pause-download" data-icon="icon-pause">Pause</li>
@ -109,6 +110,15 @@
<li id="ctx-select-all" data-key="ACCEL-KeyA" data-i18n="select-all">Select All</li>
<li id="ctx-select-invert" data-key="ACCEL-KeyI" data-i18n="invert-selection">Invert Selection</li>
<li>-</li>
<li id="ctx-import" data-icon="icon-import" data-i18n="import"></li>
<li id="ctx-export" data-icon="icon-download" data-i18n="export">
<ul class="table-context">
<li id="ctx-export-text" data-i18n="export-text"></li>
<li id="ctx-export-aria2" data-i18n="export-aria2"></li>
<li id="ctx-export-metalink" data-i18n="export-metalink"></li>
</ul>
</li>
<li>-</li>
<li id="ctx-move-top" data-key="ALT-Home" data-i18n="move-top" data-icon="icon-top">Top</li>
<li id="ctx-move-up" data-key="ALT-PageUp" data-i18n="move-up" data-icon="icon-up">Up</li>
<li id="ctx-move-down" data-key="ALT-PageDown" data-i18n="move-down" data-icon="icon-down">down</li>
@ -125,6 +135,12 @@
</p>
</template>
<template id="deletefiles-template">
<h1 class="deletefiles-title" data-i18n="deletefiles_title"></h1>
<p class="deletefiles-text" data-i18n="deletefiles_text"></p>
<ul class="deletefiles-list"></ul>
</template>
<template id="menufilter-template">
<ul>
<li id="ctx-menufilter-seperator">-</li>

View File

@ -8,6 +8,8 @@ import PORT from "./manager/port";
import { runtime } from "../lib/browser";
import { Promised } from "../lib/util";
import { PromiseSerializer } from "../lib/pserializer";
import { Keys } from "./keys";
import "./theme";
const $ = document.querySelector.bind(document);
@ -120,11 +122,15 @@ addEventListener("DOMContentLoaded", function dom() {
statusNetwork.setAttribute("title", _("statusNetwork-inactive.title"));
}
});
Keys.on("ACCEL-KeyF", () => {
$("#filter").focus();
return true;
});
});
addEventListener("contextmenu", event => {
event.preventDefault();
event.stopPropagation();
return false;
});

View File

@ -4,6 +4,7 @@
import { EventEmitter } from "../../lib/events";
// eslint-disable-next-line no-unused-vars
import { runtime, RawPort } from "../../lib/browser";
import { WindowState } from "../windowstate";
const PORT = new class Port extends EventEmitter {
port: RawPort | null;
@ -14,6 +15,17 @@ const PORT = new class Port extends EventEmitter {
if (!this.port) {
throw new Error("Could not connect");
}
new WindowState(this.port);
addEventListener("beforeunload", () => {
if (this.port) {
this.port.postMessage({
msg: "unload",
left: window.screenX,
top: window.screenY
});
}
});
this.port.onMessage.addListener((msg: any) => {
if (typeof msg === "string") {
this.emit(msg);

View File

@ -7,7 +7,7 @@ import { Prefs } from "../../lib/prefs";
import { Keys } from "../keys";
import { $ } from "../winutil";
export default class RemovalModalDialog extends ModalDialog {
export class RemovalModalDialog extends ModalDialog {
private readonly text: string;
private readonly pref: string;
@ -68,3 +68,57 @@ export default class RemovalModalDialog extends ModalDialog {
this.focusDefault();
}
}
export class DeleteFilesDialog extends ModalDialog {
private readonly paths: string[];
constructor(paths: string[]) {
super();
this.paths = paths;
}
async getContent() {
const content = $<HTMLTemplateElement>("#deletefiles-template").
content.cloneNode(true) as DocumentFragment;
await localize(content);
const list = $(".deletefiles-list", content);
for (const path of this.paths) {
const li = document.createElement("li");
li.textContent = path;
list.appendChild(li);
}
return content;
}
get buttons() {
return [
{
title: _("deletefiles_button"),
value: "ok",
default: true,
dismiss: false,
},
{
title: _("cancel"),
value: "cancel",
default: false,
dismiss: true,
}
];
}
async show() {
Keys.suppressed = true;
try {
return await super.show();
}
finally {
Keys.suppressed = false;
}
}
shown() {
this.focusDefault();
}
}

View File

@ -10,6 +10,7 @@ export const StateTexts = locale.then(() => Object.freeze(new Map([
[DownloadState.QUEUED, _("queued")],
[DownloadState.RUNNING, _("running")],
[DownloadState.FINISHING, _("finishing")],
[DownloadState.RETRYING, _("paused")],
[DownloadState.PAUSED, _("paused")],
[DownloadState.DONE, _("done")],
[DownloadState.CANCELED, _("canceled")],
@ -21,6 +22,7 @@ export const StateClasses = Object.freeze(new Map([
[DownloadState.RUNNING, "running"],
[DownloadState.FINISHING, "finishing"],
[DownloadState.PAUSED, "paused"],
[DownloadState.RETRYING, "retrying"],
[DownloadState.DONE, "done"],
[DownloadState.CANCELED, "canceled"],
[DownloadState.MISSING, "missing"],
@ -31,6 +33,7 @@ export const StateIcons = Object.freeze(new Map([
[DownloadState.RUNNING, "icon-go"],
[DownloadState.FINISHING, "icon-go"],
[DownloadState.PAUSED, "icon-pause"],
[DownloadState.RETRYING, "icon-pause"],
[DownloadState.DONE, "icon-done"],
[DownloadState.CANCELED, "icon-error"],
[DownloadState.MISSING, "icon-failed"],

Some files were not shown because too many files have changed in this diff Show More