-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auto add public trackers for magnet/torrent task #162
Changes from all commits
11fa0b4
27a2c62
04ef4d0
dca9286
9a53552
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from "./addDownloadTasksAndPoll"; | ||
export * from "./clearCachedTasks"; | ||
export * from "./torrentTracker"; | ||
export * from "./pollTasks"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import Axios from "axios"; | ||
import bencodec from "bencodec"; | ||
import { startsWithAnyProtocol, MAGNET_PROTOCOL } from "../../common/apis/protocols"; | ||
import type { State } from "../../common/state"; | ||
|
||
let cachedTrackers: string[] = []; | ||
let lastPublicTrackerURL = ""; | ||
|
||
async function updateRemoteTrackers(url: string) { | ||
let response; | ||
|
||
try { | ||
response = await Axios.get(url, { timeout: 10000 }); | ||
lastPublicTrackerURL = url; | ||
} catch (e) { | ||
console.log("Axios Error caught when updating public trackers:", e); | ||
cachedTrackers = []; | ||
} | ||
|
||
const trackerText: string = response?.data?.toString(); | ||
|
||
if (trackerText !== "") { | ||
if (trackerText.includes(",")) { | ||
cachedTrackers = trackerText.split(","); | ||
} else if (trackerText.includes("\n\n")) { | ||
cachedTrackers = trackerText.split("\n\n"); | ||
} else { | ||
cachedTrackers = trackerText.split("\n"); | ||
} | ||
console.log("successfully updated public trackers:", cachedTrackers.length); | ||
} | ||
} | ||
|
||
export function updateAndGetTorrentTrackers(storedState: State): string[] { | ||
console.debug("updateAndGetTorrentTrackers was called", new Error().stack); | ||
console.debug("cached trackers:", cachedTrackers.length); | ||
|
||
const flag = storedState.settings.torrentTrackers.enablePublicTrackers; | ||
const url = storedState.settings.torrentTrackers.publicTrackerURL; | ||
|
||
if (flag && url !== lastPublicTrackerURL) { | ||
updateRemoteTrackers(url); | ||
} | ||
|
||
return cachedTrackers; | ||
} | ||
|
||
export function setTrackers(trackers: string[]) { | ||
cachedTrackers = trackers; | ||
} | ||
|
||
export function addTrackersToURL(url: string): string { | ||
if (startsWithAnyProtocol(url, MAGNET_PROTOCOL)) { | ||
url += url.includes("?") ? "" : "?"; | ||
cachedTrackers.some((t, i) => { | ||
if (i >= 50) return true; // make sure uri is not too large | ||
url += "&tr=" + encodeURIComponent(t); | ||
return false; | ||
}); | ||
} | ||
return url; | ||
} | ||
|
||
export function addTrackersToMetaData(metaData: Buffer) { | ||
const torrent: any = bencodec.decode(metaData); | ||
cachedTrackers.forEach((t) => { | ||
torrent["announce-list"].push([Buffer.from(t, "utf8")]); | ||
}); | ||
return bencodec.encode(torrent); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ import { SessionName } from "synology-typescript-api"; | |
import { getMutableStateSingleton } from "./backgroundState"; | ||
import { getHostUrl, State } from "../common/state"; | ||
import { notify } from "../common/notify"; | ||
import { pollTasks, clearCachedTasks } from "./actions"; | ||
import { pollTasks, clearCachedTasks, updateAndGetTorrentTrackers } from "./actions"; | ||
import { assertNever } from "../common/lang"; | ||
import { filterTasks } from "../common/filtering"; | ||
|
||
|
@@ -51,6 +51,8 @@ export function onStoredStateChange(storedState: State) { | |
backgroundState.showNonErrorNotifications = | ||
storedState.settings.notifications.enableFeedbackNotifications; | ||
|
||
backgroundState.torrentTrackers = updateAndGetTorrentTrackers(storedState); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer a shift of responsibility around this call that looks kind of like the notifications block, above. Something like:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So your are suggesting not the module itself but backgroundStates to hold cachedTrackersList? I'll try it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. |
||
|
||
if (storedState.taskFetchFailureReason) { | ||
browser.browserAction.setIcon({ | ||
path: { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ import { | |
SETTING_NAMES, | ||
BadgeDisplayType, | ||
ConnectionSettings, | ||
TorrentTrackerSettings, | ||
} from "../common/state"; | ||
import { BUG_REPORT_URL } from "../common/constants"; | ||
import { ResetClientSession } from "../common/apis/messages"; | ||
|
@@ -39,6 +40,7 @@ interface Props { | |
interface State { | ||
savesFailed: boolean; | ||
rawPollingInterval: string; | ||
publicTrackerURL: string; | ||
} | ||
|
||
const POLL_MIN_INTERVAL = 15; | ||
|
@@ -52,6 +54,7 @@ function isValidPollingInterval(stringValue: string) { | |
class SettingsForm extends React.PureComponent<Props, State> { | ||
state: State = { | ||
savesFailed: false, | ||
publicTrackerURL: this.props.extensionState.settings.torrentTrackers.publicTrackerURL || "", | ||
rawPollingInterval: | ||
this.props.extensionState.settings.notifications.completionPollingInterval.toString() || | ||
POLL_DEFAULT_INTERVAL.toString(), | ||
|
@@ -164,6 +167,44 @@ class SettingsForm extends React.PureComponent<Props, State> { | |
DOWNLOAD_ONLY_PROTOCOLS.join(", "), | ||
])} | ||
/> | ||
|
||
<SettingsListCheckbox | ||
checked={this.props.extensionState.settings.torrentTrackers.enablePublicTrackers} | ||
onChange={() => { | ||
this.setTorrentTrackers( | ||
"enablePublicTrackers", | ||
!this.props.extensionState.settings.torrentTrackers.enablePublicTrackers, | ||
); | ||
}} | ||
label={browser.i18n.getMessage( | ||
"Automatically_add_trackers_to_new_tasks_form_a_trackslist_URL", | ||
boin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
)} | ||
/> | ||
|
||
<li> | ||
<span className="indent" /> | ||
<input | ||
type="text" | ||
{...disabledPropAndClassName( | ||
!this.props.extensionState.settings.torrentTrackers.enablePublicTrackers, | ||
)} | ||
style={{ flex: 1 }} | ||
value={this.state.publicTrackerURL} | ||
onChange={(e) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something in this chain of behavior needs to be debounced or otherwise delayed; as currently written, I think this is going to trigger a new request to load the tracker list on every keystroke, even while the URL is only partially written. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about "onblur" event? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure how reliably onblur fires, especially if you do something like close the settings page without clicking outside of the input box. I'm imagining one of two solutions here:
Having written these two down, I think the second is preferable: it's clearer about what's happening to the user, it follows existing UI patterns and it doesn't require much cleverness. |
||
const publicTrackerURL = e.currentTarget.value; | ||
this.setState({ publicTrackerURL }); | ||
if (publicTrackerURL !== "") { | ||
this.setTorrentTrackers("publicTrackerURL", publicTrackerURL); | ||
} | ||
}} | ||
/> | ||
{this.props.extensionState.settings.torrentTrackers.enablePublicTrackers && | ||
this.state.publicTrackerURL === "" ? ( | ||
<span className="intent-error wrong-polling-interval"> | ||
{browser.i18n.getMessage("URL_is_empty")} | ||
</span> | ||
) : undefined} | ||
</li> | ||
</SettingsList> | ||
|
||
{this.maybeRenderDebuggingOutputAndSeparator()} | ||
|
@@ -259,6 +300,19 @@ ${this.props.lastSevereError}`; | |
}); | ||
} | ||
|
||
private setTorrentTrackers<K extends keyof TorrentTrackerSettings>( | ||
key: K, | ||
value: TorrentTrackerSettings[K], | ||
) { | ||
console.log(key, value); | ||
boin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.saveSettings({ | ||
torrentTrackers: { | ||
...this.props.extensionState.settings.torrentTrackers, | ||
[key]: value, | ||
}, | ||
}); | ||
} | ||
|
||
private saveSettings = async (settings: Partial<Settings>) => { | ||
const success = await this.props.saveSettings({ | ||
...typesafePick(this.props.extensionState.settings, ...SETTING_NAMES), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a bug around this flag.
I think something should clear the list if the flag is unset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix later