12 Commits

Author SHA1 Message Date
c2b9664b4b Version 4.0.12 2019-09-13 22:31:55 +02:00
e760d2b022 Handle Chrome state changes correct. 2019-09-12 20:54:16 +02:00
1a836d914b Preroll-nope 401, 402
Closes #89
2019-09-12 13:30:06 +02:00
1b0e6eb6c4 Send referrer with prerolls
See #89
2019-09-12 13:26:24 +02:00
39827ad485 Update TODO 2019-09-12 11:22:55 +02:00
79c4d4e98f typos in the de-locale 2019-09-12 10:00:36 +02:00
427bd2f348 Actually add Italian translation 2019-09-11 23:04:39 +02:00
4fefd0e128 Arabic translation
Closes #90
2019-09-11 23:03:25 +02:00
d79060237d Silence window-type errors 2019-09-10 14:43:49 +02:00
2df7a1c592 do not force ascii in addlocale 2019-09-10 14:43:48 +02:00
8c4ceb3e4b Privetize downloads 2019-09-10 12:44:56 +02:00
bf725ece72 Open links in the correct window context 2019-09-10 12:43:47 +02:00
16 changed files with 1701 additions and 390 deletions

View File

@ -13,7 +13,6 @@ Planned for later.
* Delete files (well, as far as the browser allows)
* Inter-addon API (basic)
* Add downloads
* Chrome support
* vtable perf: cache column widths
* Download options
* This is a bit more limited, as we cannot modify options of downloads that have been started (and paused) or that are done.

View File

@ -1,4 +1,5 @@
{
"ar": "العربية [ar]",
"cs": "Čeština (CZ) [cs]",
"de": "Deutsch [de]",
"el": "Ελληνικά [el]",
@ -8,6 +9,7 @@
"fr": "Français [fr]",
"hu": "Magyar (HU) [hu]",
"id": "Bahasa Indonesia [id]",
"it": "Italiano [it]",
"ja": "日本語 (JP) [ja]",
"ko": "한국어 [ko]",
"lt": "Lietuvių [lt]",

1170
_locales/ar/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,272 +11,6 @@
"message": "Pausiert hinzufügen",
"description": "Action: Add paused"
},
"cancel": {
"message": "Abbrechen",
"description": "Button text: Cancel"
},
"canceled": {
"message": "Abgebrochen",
"description": "Download statu text"
},
"colConnections": {
"message": "Gleichzeitige Verbindungen",
"description": "Table column in prefs/network"
},
"colDomain": {
"message": "Domain",
"description": "Table column in manager"
},
"colETA": {
"message": "Verbl. Zeit",
"description": "Table column in manager"
},
"colNameURL": {
"message": "Name/URL",
"description": "Table column in manager"
},
"colPercent": {
"message": "%",
"description": "Table column in manager"
},
"colProgress": {
"message": "Fortschritt",
"description": "Table column in manager"
},
"colSegments": {
"message": "Segmente",
"description": "Table column in manager"
},
"colSize": {
"message": "Größe",
"description": "Table column in manager"
},
"colSpeed": {
"message": "Geschwindigkeit",
"description": "Table column in manager"
},
"CRASH": {
"message": "Interner Browser Fehler",
"description": "Error Message"
},
"delete": {
"message": "Entfernen",
"description": "button text"
},
"description": {
"message": "Beschreibung",
"description": "Description (keep it short); e.g. the description column in select"
},
"donate": {
"message": "Spenden!",
"description": "Donate button"
},
"done": {
"message": "Fertig",
"description": "Status text"
},
"download": {
"message": "Download",
"description": "Download (noun); e.g. Download column in select"
},
"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"
},
"fastfiltering": {
"message": "Schnelles Filtern",
"description": "Label for Fast Filtering input"
},
"FILE_FAILED": {
"message": "Dateizugriffsfehler",
"description": "Error Message"
},
"finishing": {
"message": "Beenden",
"description": "Status text"
},
"links": {
"message": "Links",
"description": "Links tab label (short); select window"
},
"mask": {
"message": "Maske",
"description": "Renaming mask (short); used in e.g. select"
},
"media": {
"message": "Medien",
"description": "Media label (short)"
},
"missing": {
"message": "Fehlt",
"description": "Status text in manager"
},
"NETWORK_FAILED": {
"message": "Netzwerkfehler",
"description": "Error Message"
},
"ok": {
"message": "OK",
"description": "Button text; Used in message boxes"
},
"paused": {
"message": "Pausiert",
"description": "Status text; manager"
},
"queued": {
"message": "Wartend",
"description": "Status text"
},
"referrer": {
"message": "Referrer",
"description": "Label for \"Referrer\""
},
"remember": {
"message": "Diese Entscheidung merken",
"description": "Checkbox text for confirmation, e.g. when removing a download in manager"
},
"rename": {
"message": "Umbenennen",
"description": "UI for renaming; currently unused"
},
"renmask": {
"message": "Umbennenungsmaske",
"description": "Renaming mask (long)"
},
"reset": {
"message": "Zurücksetzen",
"description": "Button text; pref window"
},
"running": {
"message": "Laufend",
"description": "Status text"
},
"save": {
"message": "Speichern",
"description": "Button text; e.g. prefs/Network"
},
"search": {
"message": "Suchen…",
"description": "Placeholder text; manager status search field"
},
"SERVER_BAD_CONTENT": {
"message": "Nicht gefunden",
"description": "Error message"
},
"SERVER_FAILED": {
"message": "Server-Fehler",
"description": "Error message"
},
"SERVER_FORBIDDEN": {
"message": "Nicht erlaubt",
"description": "Error message"
},
"SERVER_UNAUTHORIZED": {
"message": "Keine Berechtigung",
"description": "Error message"
},
"sizeB": {
"message": "$S$B",
"description": "Size formatting; bytes",
"placeholders": {
"s": {
"content": "$1",
"example": "100b"
}
}
},
"sizeGB": {
"message": "$S$GB",
"description": "Size formatting; giga bytes",
"placeholders": {
"s": {
"content": "$1",
"example": "100.200GB"
}
}
},
"sizeKB": {
"message": "$S$KB",
"description": "Size formatting; kilo bytes",
"placeholders": {
"s": {
"content": "$1",
"example": "100.2KB"
}
}
},
"sizeMB": {
"message": "$S$MB",
"description": "Size formatting; mega bytes",
"placeholders": {
"s": {
"content": "$1",
"example": "100.22MB"
}
}
},
"sizePB": {
"message": "$S$PB",
"description": "Size formatting; peta bytes (you never know)",
"placeholders": {
"s": {
"content": "$1",
"example": "100.212PB"
}
}
},
"sizeTB": {
"message": "$S$TB",
"description": "Size formatting; tera bytes (you never know)",
"placeholders": {
"s": {
"content": "$1",
"example": "100.002TB"
}
}
},
"speedB": {
"message": "$SPEED$b/s",
"description": "Speed formatting; bytes",
"placeholders": {
"speed": {
"content": "$1",
"example": "100b/s"
}
}
},
"speedKB": {
"message": "$SPEED$KB/s",
"description": "Speed formatting; kilo bytes",
"placeholders": {
"speed": {
"content": "$1",
"example": "100.1KB/s"
}
}
},
"speedMB": {
"message": "$SPEED$MB/s",
"description": "Speed formatting; mega bytes",
"placeholders": {
"speed": {
"content": "$1",
"example": "100.20MB/s"
}
}
},
"title": {
"message": "Titel",
"description": "Column text; Title label (short)"
},
"unlimited": {
"message": "Unbegrenzt",
"description": "Option text; Prefs/Network"
},
"useonlyonce": {
"message": "Einmalig",
"description": "Label for Use-Once checkboxes"
},
"add_download": {
"message": "Download hinzufügen",
"description": "Action for adding a download"
@ -329,6 +63,14 @@
"message": "Batch Download",
"description": "Messagebox title for batch confirmations"
},
"cancel": {
"message": "Abbrechen",
"description": "Button text: Cancel"
},
"canceled": {
"message": "Abgebrochen",
"description": "Download status text"
},
"cancel_download": {
"message": "Abbrechen",
"description": "Action to cancel downloads, e.g. from the context menu"
@ -342,9 +84,45 @@
"description": "Checkbox label text for decision confirmations"
},
"check_selected_items": {
"message": "Ausgewählte Einträge makieren",
"message": "Ausgewählte Einträge markieren",
"description": "Menu text"
},
"colConnections": {
"message": "Gleichzeitige Verbindungen",
"description": "Table column in prefs/network"
},
"colDomain": {
"message": "Domain",
"description": "Table column in manager"
},
"colETA": {
"message": "Verbl. Zeit",
"description": "Table column in manager"
},
"colNameURL": {
"message": "Name/URL",
"description": "Table column in manager"
},
"colPercent": {
"message": "%",
"description": "Table column in manager"
},
"colProgress": {
"message": "Fortschritt",
"description": "Table column in manager"
},
"colSegments": {
"message": "Segmente",
"description": "Table column in manager"
},
"colSize": {
"message": "Größe",
"description": "Table column in manager"
},
"colSpeed": {
"message": "Geschwindigkeit",
"description": "Table column in manager"
},
"conflict_overwrite": {
"message": "Überschreiben",
"description": "Option text; prefs/general"
@ -357,6 +135,10 @@
"message": "Umbenennen",
"description": "Option text; prefs/general"
},
"CRASH": {
"message": "Interner Browser Fehler",
"description": "Error Message"
},
"create_filter": {
"message": "Filter erstellen",
"description": "Button text; Create filter dialog; prefs/filters"
@ -405,26 +187,42 @@
"message": "Videos (mp4, webm, mkv, …)",
"description": "Filter label for the Videos filter"
},
"delete": {
"message": "Entfernen",
"description": "button text"
},
"description": {
"message": "Beschreibung",
"description": "Description (keep it short); e.g. the description column in select"
},
"disable_other_filters": {
"message": "Andere deaktivieren",
"description": "Checkbox label. Keep it short"
},
"donate": {
"message": "Spenden!",
"description": "Donate button"
},
"done": {
"message": "Fertig",
"description": "Status text"
},
"download": {
"message": "Download",
"description": "Download (noun); e.g. Download column in select"
},
"download_verb": {
"message": "Download",
"description": "Download (verb/action); e.g. in single and select buttons"
},
"dta_regular_all": {
"message": "DownThemAll! - Alle Tabs",
"description": "Menu text"
},
"dta_turbo_all": {
"message": "OneClick! - Alle Tabs",
"description": "Menu text"
},
"dta_regular": {
"message": "DownThemAll!",
"description": "Regular dta action; Menu text"
},
"dta_regular_all": {
"message": "DownThemAll! - Alle Tabs",
"description": "Menu text"
},
"dta_regular_image": {
"message": "Bild mit DownThemAll! speichern",
"description": "Menu text"
@ -445,6 +243,10 @@
"message": "OneClick!",
"description": "OneClick! action; Menu text"
},
"dta_turbo_all": {
"message": "OneClick! - Alle Tabs",
"description": "Menu text"
},
"dta_turbo_image": {
"message": "Bild mit OneClick! speichern",
"description": "Menu text"
@ -477,16 +279,28 @@
"message": "Nichts ausgewählt",
"description": "Error Message; select window"
},
"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"
},
"fastfiltering": {
"message": "Schnelles Filtern",
"description": "Label for Fast Filtering input"
},
"fastfilter_placeholder": {
"message": "Platzhalter-Ausdruck oder Regular Expression",
"description": "Placeholder for fastfilter inputs"
},
"FILE_FAILED": {
"message": "Dateizugriffsfehler",
"description": "Error Message"
},
"filter_at_least_one": {
"message": "Mindestens einen Filter-Typ auswählen!",
"description": "Error message when no filter types are selected for a filter in the preferences UI"
},
"filter_create_title": {
"message": "Neuen Filter erstelln",
"message": "Neuen Filter erstellen",
"description": "Message box title"
},
"filter_expression": {
@ -497,6 +311,10 @@
"message": "Filter-Titel",
"description": "Message box label"
},
"filter_types": {
"message": "Filter Typen",
"description": "Message box label"
},
"filter_type_link": {
"message": "Link Filter",
"description": "Message box checkbox label"
@ -505,9 +323,9 @@
"message": "Medien Filter",
"description": "Message box checkbox label"
},
"filter_types": {
"message": "Filter Typen",
"description": "Message box label"
"finishing": {
"message": "Beenden",
"description": "Status text"
},
"force_start": {
"message": "Start erzwingen",
@ -557,6 +375,14 @@
"message": "Begrenzt auf",
"description": "Label text; used in prefs/network"
},
"links": {
"message": "Links",
"description": "Links tab label (short); select window"
},
"manager_short": {
"message": "Manager",
"description": "Menu text"
},
"manager_status_items": {
"message": "$COMPLETE$ von $TOTAL$ Downloads beendet ($SHOWING$ angezeigt), $RUNNING$ laufend",
"description": "Status bar text; manager",
@ -579,18 +405,26 @@
}
}
},
"manager_short": {
"message": "Manager",
"description": "Menu text"
},
"manager_title": {
"message": "DownThemAll! Manager",
"description": "Window/tab title"
},
"mask": {
"message": "Maske",
"description": "Renaming mask (short); used in e.g. select"
},
"mask_default": {
"message": "Standard-Maske",
"description": "Status text; Used in the mask column, select window"
},
"media": {
"message": "Medien",
"description": "Media label (short)"
},
"missing": {
"message": "Fehlt",
"description": "Status text in manager"
},
"move_bottom": {
"message": "Ende",
"description": "Action for moving a download to the bottom"
@ -617,18 +451,22 @@
}
}
},
"NETWORK_FAILED": {
"message": "Netzwerkfehler",
"description": "Error Message"
},
"never_ask_again": {
"message": "Nicht wieder fragen",
"description": "Donation button"
},
"no_links": {
"message": "Keine Links gefunden!",
"description": "Notification text"
},
"noitems_label": {
"message": "Nichts ausgewählt",
"description": "Status bar text in select"
},
"no_links": {
"message": "Keine Links gefunden!",
"description": "Notification text"
},
"numitems_label": {
"message": "$ITEMS$ Downloads ausgewählt",
"description": "Status bar text in select; Number of items selected (label)",
@ -639,6 +477,10 @@
}
}
},
"ok": {
"message": "OK",
"description": "Button text; Used in message boxes"
},
"open_directory": {
"message": "Verzeichnis öffnen",
"description": "Menu text; manager context"
@ -663,10 +505,26 @@
"message": "Netzwerk",
"description": "Pref tab text"
},
"paused": {
"message": "Pausiert",
"description": "Status text; manager"
},
"pause_download": {
"message": "Pausieren",
"description": "Action for pausing a download"
},
"prefs_conflicts": {
"message": "Wenn eine Datei bereits existiert",
"description": "Preferences/General; group text"
},
"prefs_short": {
"message": "Einstellungen",
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "DownThemAll! Einstellungen",
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "Neue Downloads immer pausiert hinzufügen, anstatt sie direkt zu starten",
"description": "Preferences/General"
@ -687,14 +545,26 @@
"message": "Keine allgemeinen Menü-Eintrage anzeigen",
"description": "Preferences/General"
},
"pref_manager": {
"message": "Manager",
"description": "Preferences/General; group text"
},
"pref_manager_tooltip": {
"message": "Keine Tooltips im Manager-Tab anzeigen",
"description": "Preferences/General"
},
"pref_netglobal": {
"message": "Allgemeine Netzwerk-Beschränkungen",
"description": "Preferences/General; group text"
},
"pref_open_manager_on_queue": {
"message": "Den Manager öffnen nachdem neue Downloads zur Warteschlange hinzugefügt wurden",
"description": "Preferences/General"
},
"pref_queueing": {
"message": "Download-Warteschlange",
"description": "Preferences/General; group text"
},
"pref_queue_notification": {
"message": "Benachrichtigung anzeigen, wenn neue Downloads hinzugefügt wurden",
"description": "Preferences/General"
@ -711,37 +581,13 @@
"message": "Versuche Text-Links in Webseiten zu finden (langsamer)",
"description": "Preferences/General"
},
"pref_manager": {
"message": "Manager",
"description": "Preferences/General; group text"
},
"pref_netglobal": {
"message": "Allgemeine Netzwerk-Beschränkungen",
"description": "Preferences/General; group text"
},
"pref_queueing": {
"message": "Download-Warteschlange",
"description": "Preferences/General; group text"
},
"pref_ui": {
"message": "Benutzeroberfläche",
"description": "Preferences/General; group text"
},
"prefs_conflicts": {
"message": "Wenn eine Datei bereits existiert",
"description": "Preferences/General; group text"
},
"prefs_short": {
"message": "Einstellungen",
"description": "Menu text; Preferences"
},
"prefs_title": {
"message": "DownThemAll! Einstellungen",
"description": "Window/tab title; Preferences"
},
"queue_finished": {
"message": "Die Download-Warteschlange ist fertig",
"description": "Notification text"
"queued": {
"message": "Wartend",
"description": "Status text"
},
"queued_download": {
"message": "Ein Download hinzugefügt!",
@ -757,6 +603,18 @@
}
}
},
"queue_finished": {
"message": "Die Download-Warteschlange ist fertig",
"description": "Notification text"
},
"referrer": {
"message": "Referrer",
"description": "Label for \"Referrer\""
},
"remember": {
"message": "Diese Entscheidung merken",
"description": "Checkbox text for confirmation, e.g. when removing a download in manager"
},
"remove_all_complete_downloads": {
"message": "Alle fertigen entfernen",
"description": "Menu text"
@ -831,18 +689,18 @@
"message": "Download entfernen",
"description": "Action for removing a download, no matter what state"
},
"remove_download_question": {
"message": "Wirklich alle ausgewählten Downloads entfernen?",
"description": "Messagebox text"
},
"remove_downloads": {
"message": "Downloas entfernen",
"message": "Downloads entfernen",
"description": "Menu text"
},
"remove_downloads_title": {
"message": "Wirklich Downloads entfernen?",
"description": "Messagebox title; manager"
},
"remove_download_question": {
"message": "Wirklich alle ausgewählten Downloads entfernen?",
"description": "Messagebox text"
},
"remove_failed_downloads": {
"message": "Fehlgeschlagene entfernen",
"description": "Menu text"
@ -889,6 +747,10 @@
"message": "Ausgewählte entfernen",
"description": "Menu text"
},
"rename": {
"message": "Umbenennen",
"description": "UI for renaming; currently unused"
},
"renamer_batch": {
"message": "Batch Nummer",
"description": "Mask text; see mask button"
@ -1005,6 +867,14 @@
"message": "Datum hinzugefügt - Jahr",
"description": "Mask text; see mask button"
},
"renmask": {
"message": "Umbenennungsmaske",
"description": "Renaming mask (long)"
},
"reset": {
"message": "Zurücksetzen",
"description": "Button text; pref window"
},
"reset_confirmations": {
"message": "Gemerkte Entscheidungen zurücksetzen",
"description": "Button text; pref/General"
@ -1025,6 +895,18 @@
"message": "Fortsetzen",
"description": "Action for resuming a download"
},
"running": {
"message": "Laufend",
"description": "Status text"
},
"save": {
"message": "Speichern",
"description": "Button text; e.g. prefs/Network"
},
"search": {
"message": "Suchen…",
"description": "Placeholder text; manager status search field"
},
"select_all": {
"message": "Alles auswählen",
"description": "Menu text; e.g. select context"
@ -1041,6 +923,22 @@
"message": "DownThemAll! - Downloads auswählen",
"description": "Title of the select window"
},
"SERVER_BAD_CONTENT": {
"message": "Nicht gefunden",
"description": "Error message"
},
"SERVER_FAILED": {
"message": "Server-Fehler",
"description": "Error message"
},
"SERVER_FORBIDDEN": {
"message": "Nicht erlaubt",
"description": "Error message"
},
"SERVER_UNAUTHORIZED": {
"message": "Keine Berechtigung",
"description": "Error message"
},
"set_mask": {
"message": "Umbenennungsmaske setzen",
"description": "Menu text; select window"
@ -1050,30 +948,62 @@
"description": "Header text; single window"
},
"single_header": {
"message": "Download URL (link) und andere Optionen eingeben",
"message": "Download URL (Link) und andere Optionen eingeben",
"description": "Header text; single window"
},
"single_title": {
"message": "DownThemAll! - Link hinzufügen",
"description": "Title of single window"
},
"size_progress": {
"message": "$WRITTEN$ von $TOTAL$",
"description": "Status text; manager size column",
"sizeB": {
"message": "$S$B",
"description": "Size formatting; bytes",
"placeholders": {
"total": {
"content": "$2",
"example": ""
},
"written": {
"s": {
"content": "$1",
"example": ""
"example": "100b"
}
}
},
"size_unknown": {
"message": "Unbekannt",
"description": "Status text; manager size column"
"sizeGB": {
"message": "$S$GB",
"description": "Size formatting; giga bytes",
"placeholders": {
"s": {
"content": "$1",
"example": "100.200GB"
}
}
},
"sizeKB": {
"message": "$S$KB",
"description": "Size formatting; kilo bytes",
"placeholders": {
"s": {
"content": "$1",
"example": "100.2KB"
}
}
},
"sizeMB": {
"message": "$S$MB",
"description": "Size formatting; mega bytes",
"placeholders": {
"s": {
"content": "$1",
"example": "100.22MB"
}
}
},
"sizePB": {
"message": "$S$PB",
"description": "Size formatting; peta bytes (you never know)",
"placeholders": {
"s": {
"content": "$1",
"example": "100.212PB"
}
}
},
"sizes_huge": {
"message": "Riesig (> $HIGH$)",
@ -1127,6 +1057,64 @@
}
}
},
"sizeTB": {
"message": "$S$TB",
"description": "Size formatting; tera bytes (you never know)",
"placeholders": {
"s": {
"content": "$1",
"example": "100.002TB"
}
}
},
"size_progress": {
"message": "$WRITTEN$ von $TOTAL$",
"description": "Status text; manager size column",
"placeholders": {
"total": {
"content": "$2",
"example": ""
},
"written": {
"content": "$1",
"example": ""
}
}
},
"size_unknown": {
"message": "Unbekannt",
"description": "Status text; manager size column"
},
"speedB": {
"message": "$SPEED$b/s",
"description": "Speed formatting; bytes",
"placeholders": {
"speed": {
"content": "$1",
"example": "100b/s"
}
}
},
"speedKB": {
"message": "$SPEED$KB/s",
"description": "Speed formatting; kilo bytes",
"placeholders": {
"speed": {
"content": "$1",
"example": "100.1KB/s"
}
}
},
"speedMB": {
"message": "$SPEED$MB/s",
"description": "Speed formatting; mega bytes",
"placeholders": {
"speed": {
"content": "$1",
"example": "100.20MB/s"
}
}
},
"statusNetwork_active_title": {
"message": "Neue Downloads werden gestartet",
"description": "Status bar tooltip; manager network icon"
@ -1135,8 +1123,12 @@
"message": "Neue Downloads werden nicht gestartet",
"description": "Status bar tooltip; manager network icon"
},
"title": {
"message": "Titel",
"description": "Column text; Title label (short)"
},
"toggle_selected_items": {
"message": "Markierungen für Auswahl umgekehren",
"message": "Markierungen für Auswahl umkehren",
"description": "Menu text; select"
},
"tooltip_date": {
@ -1166,5 +1158,13 @@
"uncheck_selected_items": {
"message": "Markierung von Auswahl entfernen",
"description": "Menu text; select"
},
"unlimited": {
"message": "Unbegrenzt",
"description": "Option text; Prefs/Network"
},
"useonlyonce": {
"message": "Einmalig",
"description": "Label for Use-Once checkboxes"
}
}

View File

@ -48,6 +48,9 @@ const CHROME_CONTEXTS = Object.freeze(new Set([
async function runContentJob(tab: Tab, file: string, msg: any) {
try {
if (tab && tab.incognito && msg) {
msg.private = tab.incognito;
}
const res = await tabs.executeScript(tab.id, {
file,
allFrames: true,

View File

@ -19,6 +19,7 @@ export interface MessageSender {
export interface Tab {
id?: number;
incognito?: boolean;
}
export interface MenuClickInfo {
@ -39,9 +40,61 @@ export interface RawPort {
postMessage: (message: any) => void;
}
interface WebRequestFilter {
urls?: string[];
}
interface WebRequestListener {
addListener(
callback: Function,
filter: WebRequestFilter,
extraInfoSpec: string[]
): void;
removeListener(callback: Function): void;
}
type Header = {name: string; value: string};
export interface DownloadOptions {
conflictAction: string;
filename: string;
saveAs: boolean;
url: string;
method?: string;
body?: string;
incognito?: boolean;
headers: Header[];
}
export interface DownloadsQuery {
id?: number;
}
interface Downloads {
download(download: DownloadOptions): Promise<number>;
open(manId: number): Promise<void>;
show(manId: number): Promise<void>;
pause(manId: number): Promise<void>;
resume(manId: number): Promise<void>;
cancel(manId: number): Promise<void>;
erase(query: DownloadsQuery): Promise<void>;
search(query: DownloadsQuery): Promise<any[]>;
getFileIcon(id: number, options?: any): Promise<string>;
setShelfEnabled(state: boolean): void;
onCreated: ExtensionListener;
onChanged: ExtensionListener;
onErased: ExtensionListener;
}
interface WebRequest {
onBeforeSendHeaders: WebRequestListener;
onSendHeaders: WebRequestListener;
onHeadersReceived: WebRequestListener;
}
export const {browserAction} = polyfill;
export const {contextMenus} = polyfill;
export const {downloads} = polyfill;
export const {downloads}: {downloads: Downloads} = polyfill;
export const {extension} = polyfill;
export const {history} = polyfill;
export const {menus} = polyfill;
@ -51,7 +104,7 @@ export const {sessions} = polyfill;
export const {storage} = polyfill;
export const {tabs} = polyfill;
export const {webNavigation} = polyfill;
export const {webRequest} = polyfill;
export const {webRequest}: {webRequest: WebRequest} = polyfill;
export const {windows} = polyfill;
export const CHROME = navigator.appVersion.includes("Chrome/");

View File

@ -1,7 +1,8 @@
"use strict";
// License: MIT
import { CHROME, downloads } from "../browser";
// eslint-disable-next-line no-unused-vars
import { CHROME, downloads, DownloadOptions } from "../browser";
import { Prefs } from "../prefs";
import { PromiseSerializer } from "../pserializer";
import { filterInSitu, parsePath } from "../util";
@ -22,18 +23,6 @@ import {
} from "./state";
import { Preroller } from "./preroller";
type Header = {name: string; value: string};
interface Options {
conflictAction: string;
filename: string;
saveAs: boolean;
url: string;
method?: string;
body?: string;
incognito?: boolean;
headers: Header[];
}
export class Download extends BaseDownload {
public manager: Manager;
@ -76,23 +65,23 @@ export class Download extends BaseDownload {
if (this.manId) {
const {manId: id} = this;
try {
const state = await downloads.search({id});
if (state[0].state === "in_progress") {
const state = (await downloads.search({id})).pop() || {};
if (state.state === "in_progress" && !state.error && !state.paused) {
this.changeState(RUNNING);
this.updateStateFromBrowser();
return;
}
if (state[0].state === "complete") {
if (state.state === "complete") {
this.changeState(DONE);
this.updateStateFromBrowser();
return;
}
if (!state[0].canResume) {
if (!state.canResume) {
throw new Error("Cannot resume");
}
// Cannot await here
// Firefox bug: will not return until download is finished
downloads.resume(id).catch(() => {});
downloads.resume(id).catch(console.error);
this.changeState(RUNNING);
return;
}
@ -120,7 +109,7 @@ export class Download extends BaseDownload {
return;
}
}
const options: Options = {
const options: DownloadOptions = {
conflictAction: await Prefs.get("conflict-action"),
filename: this.dest.full,
saveAs: false,
@ -140,6 +129,12 @@ export class Download extends BaseDownload {
value: this.referrer
});
}
else if (CHROME) {
options.headers.push({
name: "X-DTA-ID",
value: this.sessionId.toString(),
});
}
if (this.manId) {
this.manager.removeManId(this.manId);
}
@ -319,7 +314,10 @@ export class Download extends BaseDownload {
this.markDirty();
switch (state.state) {
case "in_progress":
if (error) {
if (state.paused) {
this.changeState(PAUSED);
}
else if (error) {
this.cancel();
this.error = error;
}

View File

@ -16,8 +16,9 @@ import { Download } from "./download";
import { ManagerPort } from "./port";
import { Scheduler } from "./scheduler";
import { Limits } from "./limits";
import { downloads, runtime } from "../browser";
import { downloads, runtime, webRequest, CHROME } from "../browser";
const US = runtime.getURL("");
const AUTOSAVE_TIMEOUT = 2000;
const DIRTY_TIMEOUT = 100;
@ -83,6 +84,14 @@ export class Manager extends EventEmitter {
Limits.on("changed", () => {
this.resetScheduler();
});
if (CHROME) {
webRequest.onBeforeSendHeaders.addListener(
this.stuffReferrer.bind(this),
{urls: ["<all_urls>"]},
["blocking", "requestHeaders", "extraHeaders"]
);
}
}
async init() {
@ -384,6 +393,31 @@ export class Manager extends EventEmitter {
getMsgItems() {
return this.items.map(e => e.toMsg());
}
stuffReferrer(details: any): any {
if (details.tabId > 0 && !US.startsWith(details.initiator)) {
return undefined;
}
const sidx = details.requestHeaders.findIndex(
(e: any) => e.name.toLowerCase() === "x-dta-id");
if (sidx < 0) {
return undefined;
}
const sid = parseInt(details.requestHeaders[sidx].value, 10);
details.requestHeaders.splice(sidx, 1);
const item = this.sids.get(sid);
if (!item) {
return undefined;
}
details.requestHeaders.push({
name: "Referer",
value: (item.uReferrer || item.uURL).toString()
});
const rv: any = {
requestHeaders: details.requestHeaders
};
return rv;
}
}
let inited: Promise<Manager>;

View File

@ -13,6 +13,17 @@ const PREROLL_HEURISTICS = /dl|attach|download|name|file|get|retr|^n$|\.(php|asp
const PREROLL_HOSTS = /4cdn|chan/;
const PREROLL_TIMEOUT = 10000;
const PREROLL_NOPE = new Set<string>();
/* eslint-disable no-magic-numbers */
const NOPE_STATUSES = Object.freeze(new Set([
400,
401,
402,
405,
416,
]));
/* eslint-enable no-magic-numbers */
const PREROLL_SEARCHEXTS = Object.freeze(new Set<string>([
"php",
"asp",
@ -79,7 +90,7 @@ export class Preroller {
private async prerollFirefox() {
const controller = new AbortController();
const {signal} = controller;
const {uURL} = this.download;
const {uURL, uReferrer} = this.download;
const res = await fetch(uURL.toString(), {
method: "GET",
headers: new Headers({
@ -87,6 +98,7 @@ export class Preroller {
}),
mode: "same-origin",
signal,
referrer: (uReferrer || uURL).toString(),
});
if (res.body) {
res.body.cancel();
@ -98,7 +110,7 @@ export class Preroller {
private async prerollChrome() {
let rid = "";
const {uURL} = this.download;
const {uURL, uReferrer} = this.download;
const rurl = uURL.toString();
let listener: any;
const wr = new Promise<any[]>(resolve => {
@ -132,9 +144,11 @@ export class Preroller {
const res = await fetch(rurl, {
method: "GET",
headers: new Headers({
Range: "bytes=0-1",
"Range": "bytes=0-1",
"X-DTA-ID": this.download.sessionId.toString(),
}),
signal,
referrer: (uReferrer || uURL).toString(),
});
if (res.body) {
res.body.cancel();
@ -204,7 +218,7 @@ export class Preroller {
else if (status === 402 || status === 407) {
rv.error = "SERVER_UNAUTHORIZED";
}
else if (status === 400 || status === 405 || status === 416) {
else if (NOPE_STATUSES.has(status)) {
PREROLL_NOPE.add(this.download.uURL.host);
if (PREROLL_NOPE.size > 1000) {
PREROLL_NOPE.delete(PREROLL_NOPE.keys().next().value);

View File

@ -187,8 +187,8 @@ export async function select(links: BaseItem[], media: BaseItem[]) {
openPrefs();
});
port.on("openUrls", ({urls}) => {
openUrls(urls);
port.on("openUrls", ({urls, incognito}) => {
openUrls(urls, incognito);
});
try {

View File

@ -8,37 +8,45 @@ import DEFAULT_ICONS from "../data/icons.json";
const DONATE_URL = "https://www.downthemall.org/howto/donate/";
const MANAGER_URL = "/windows/manager.html";
export async function mostRecentBrowser(): Promise<any> {
export async function mostRecentBrowser(incognito: boolean): Promise<any> {
let window;
try {
window = await windows.getCurrent({windowTypes: ["normal"]});
window = await windows.getCurrent();
if (window.type !== "normal") {
throw new Error("not a normal window");
}
if (incognito && !window.incognito) {
throw new Error("Not incognito");
}
}
catch {
try {
window = await windows.getlastFocused({windowTypes: ["normal"]});
window = await windows.getlastFocused();
if (window.type !== "normal") {
throw new Error("not a normal window");
}
if (incognito && !window.incognito) {
throw new Error("Not incognito");
}
}
catch {
window = Array.from(await windows.getAll({windowTypes: ["normal"]})).
filter((w: any) => w.type === "normal").pop();
filter(
(w: any) => w.type === "normal" && !!w.incognito === !!incognito).
pop();
}
}
if (!window) {
window = await windows.create({
url: DONATE_URL,
incognito: !!incognito,
type: "normal",
});
}
return window;
}
export async function openInTab(url: string) {
const window = await mostRecentBrowser();
export async function openInTab(url: string, incognito: boolean) {
const window = await mostRecentBrowser(incognito);
await tabs.create({
active: true,
url,
@ -47,7 +55,7 @@ export async function openInTab(url: string) {
await windows.update(window.id, {focused: true});
}
export async function openInTabOrFocus(url: string) {
export async function openInTabOrFocus(url: string, incognito: boolean) {
const etabs = await tabs.query({
url
});
@ -57,21 +65,21 @@ export async function openInTabOrFocus(url: string) {
await windows.update(tab.windowId, {focused: true});
return;
}
await openInTab(url);
await openInTab(url, incognito);
}
export async function maybeOpenInTab(url: string) {
export async function maybeOpenInTab(url: string, incognito: boolean) {
const etabs = await tabs.query({
url
});
if (etabs.length) {
return;
}
await openInTab(url);
await openInTab(url, incognito);
}
export async function donate() {
await openInTab(DONATE_URL);
await openInTab(DONATE_URL, false);
}
export async function openPrefs() {
@ -86,15 +94,15 @@ export async function openManager(focus = true) {
console.error(ex.toString(), ex);
}
if (focus) {
await openInTabOrFocus(await runtime.getURL(MANAGER_URL));
await openInTabOrFocus(await runtime.getURL(MANAGER_URL), false);
}
else {
await maybeOpenInTab(await runtime.getURL(MANAGER_URL));
await maybeOpenInTab(await runtime.getURL(MANAGER_URL), false);
}
}
export async function openUrls(urls: string) {
const window = await mostRecentBrowser();
export async function openUrls(urls: string, incognito: boolean) {
const window = await mostRecentBrowser(incognito);
for (const url of urls) {
try {
await tabs.create({

View File

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "DownThemAll!",
"version": "4.0.11",
"version": "4.0.12",
"description": "__MSG_extensionDescription__",
"homepage_url": "https://downthemall.org/",
@ -34,7 +34,8 @@
"storage",
"tabs",
"webNavigation",
"webRequest"
"webRequest",
"webRequestBlocking"
],
"background": {

View File

@ -77,6 +77,8 @@ function urlToUsable(e: any, u: string) {
}
class Gatherer {
private: boolean;
textLinks: boolean;
selectionOnly: boolean;
@ -88,6 +90,7 @@ class Gatherer {
transferable: string[];
constructor(options: any) {
this.private = !!options.private;
this.textLinks = options.textLinks;
this.selectionOnly = options.selectionOnly;
this.selection = options.selectionOnly ? getSelection() : null;
@ -255,6 +258,7 @@ class Gatherer {
return {
url: url.href,
title,
private: this.private
};
}
catch (ex) {

View File

@ -70,7 +70,7 @@ def main():
if modified:
try:
with open("messages.json.tmp", "w", encoding="utf-8") as outp:
json.dump(data, outp, sort_keys=True, indent=2)
json.dump(data, outp, sort_keys=True, indent=2, ensure_ascii=False)
os.rename("messages.json.tmp", "_locales/en/messages.json")
finally:
try:

View File

@ -26,7 +26,7 @@ UNCOMPRESSABLE = set((".png", ".jpg", ".zip", ".woff2"))
LICENSED = set((".css", ".html", ".js", "*.ts"))
IGNORED = set((".DS_Store", "Thumbs.db"))
PERM_IGNORED_FX = set(("downloads.shelf", "webRequest"))
PERM_IGNORED_FX = set(("downloads.shelf", "webRequest", "webRequestBlocking"))
PERM_IGNORED_CHROME = set(("menus", "sessions"))
SCRIPTS = [

View File

@ -428,17 +428,42 @@ class SelectionTable extends VirtualTable {
}
openSelection() {
const items = this.items.filter((i, idx) => this.selection.contains(idx));
if (!items.length) {
const privates: BaseMatchedItem[] = [];
const items = this.items.filter((i, idx) => this.selection.contains(idx)).
filter(i => {
if (i.private) {
privates.push(i);
return false;
}
return true;
});
if (!items.length && !privates.length) {
if (this.focusRow < 0) {
return;
}
items.push(this.items.at(this.focusRow));
const item = this.items.at(this.focusRow);
if (item.private) {
privates.push(item);
}
else {
items.push(item);
}
}
if (items.length) {
PORT.postMessage({
msg: "openUrls",
urls: items.map(e => e.url),
incognito: false,
});
}
if (privates.length) {
PORT.postMessage({
msg: "openUrls",
urls: privates.map(e => e.url),
incognito: true,
});
}
PORT.postMessage({
msg: "openUrls",
urls: items.map(e => e.url)
});
}
applyDeltaTo(delta: ItemDelta[], items: ItemCollection) {