Skip to content

Commit

Permalink
Handle magnet links (#134)
Browse files Browse the repository at this point in the history
* support for registering magnet url

* open upload dialog if manget url present

* misc. improvements

* enforce https
  • Loading branch information
bill-ahmed authored Sep 2, 2021
1 parent 8780cc3 commit 70adae6
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 9 deletions.
20 changes: 19 additions & 1 deletion src/app/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,19 @@ export class HomeComponent implements OnInit {
this.getUserPreferences();
this.isDarkTheme = this.theme.getThemeSubscription();
this.getQbitBuildInfo();

// Check if user was led here via a magnet download link
this.handleMagnetURLCheck();
}

/** Open the modal for adding a new torrent */
openAddTorrentDialog(): void {
openAddTorrentDialog(opts?: any): void {
const addTorDialogRef = this.dialog.open(AddTorrentDialogComponent,
{
disableClose: true,
panelClass: "generic-dialog",
minWidth: "40%",
data: opts
}
);

Expand Down Expand Up @@ -79,6 +83,20 @@ export class HomeComponent implements OnInit {
handleAddTorrentDialogClosed(data: any): void {
}

handleMagnetURLCheck() {
let downloadHash = '#download=';

// Make sure it exists and is at the front
if(location.hash.indexOf(downloadHash) !== 0)
return;

// Now that it exists, we can take it by reading up to downloadHash's length
let target_url = decodeURIComponent(location.hash.substring(downloadHash.length));
history.replaceState('', document.title, location.pathname);

this.openAddTorrentDialog({ magnetURL: target_url });
}

public toggleTheme(): void {
this.theme.setDarkTheme(!this.theme.getCurrentValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="mat-dialog-inner-container mat-app-background upload_container">
<h2 mat-dialog-title>Upload Torrents</h2>
<mat-dialog-content class="mat-typography dialog_content">
<mat-tab-group mat-align-tabs="start" (selectedTabChange)="handleTabChange($event)">
<mat-tab-group mat-align-tabs="start" (selectedTabChange)="handleTabChange($event)" [selectedIndex]="currentTab?.index || 0">
<mat-tab label="File Upload">
<div class="file_upload_container">
<br/>
Expand Down
20 changes: 15 additions & 5 deletions src/app/modals/add-torrent-dialog/add-torrent-dialog.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { Component, Inject, OnInit } from '@angular/core';

// Material UI Components
import { MatFormField } from '@angular/material/form-field';
import { MatDialogRef, MatDialog } from '@angular/material/dialog';
import { MatDialogRef, MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { TorrentDataStoreService } from '../../services/torrent-management/torrent-data-store.service';
import { FileSystemDialogComponent } from '../file-system-dialog/file-system-dialog.component';
import { ThemeService } from '../../services/theme.service';
Expand Down Expand Up @@ -34,16 +34,24 @@ export class AddTorrentDialogComponent implements OnInit {
public serialized_nodes: SerializedNode[] = [];

/** Keep track of the mat-tab the user is currently in. */
private currentTab: MatTabChangeEvent;
public currentTab: MatTabChangeEvent;
private fileSystemExplorerDialogREF: MatDialogRef<FileSystemDialogComponent, any>;

private inputData: any;

constructor(private appConfig: ApplicationConfigService, private dialogRef:MatDialogRef<AddTorrentDialogComponent>, private data_store: TorrentDataStoreService,
private torrentParser: TorrentParserService, public fileSystemDialog: MatDialog, public snackbar: SnackbarService, private theme: ThemeService) { }
private torrentParser: TorrentParserService, public fileSystemDialog: MatDialog, public snackbar: SnackbarService, private theme: ThemeService,
@Inject(MAT_DIALOG_DATA) inputData) { this.inputData = inputData }

ngOnInit(): void {
this.isDarkTheme = this.theme.getThemeSubscription();
this.updateDefaultSaveLocationFromDisk();
this.appConfig.getUserPreferences().then(pref => { this.show_torrent_contents = pref.web_ui_options.upload_torrents?.show_parsed_torrents_from_file ?? true });

if(this.inputData?.magnetURL) {
this.urlsToUpload = this.inputData.magnetURL;
this.handleTabChange({ index: 1 } as any)
}
}

handleTabChange(event: MatTabChangeEvent) {
Expand Down Expand Up @@ -93,7 +101,9 @@ export class AddTorrentDialogComponent implements OnInit {

} catch (error) {
console.error('uploaded magnet URLs!', error);
this.snackbar.enqueueSnackBar("Error uploding torrents, check console log for details.", { type: 'error' });

if(error?.status !== 200 && error?.statusText !== 'OK')
this.snackbar.enqueueSnackBar("Error uploding torrents, check console log for details.", { type: 'error' });
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ <h3> Notifications </h3>
</div>
</section >
<br/>

<section>
<h3> Events </h3>

<div class="field">
<button mat-stroked-button (click)="registerMagnetHandler()">
<mat-icon>public</mat-icon>
Register to handle magnet links...
</button>
</div>
</section >
<br/>
</mat-tab>

<mat-tab label="Torrents">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FormControl, Validators } from '@angular/forms';
import { AuthService } from 'src/app/services/auth/auth.service';
import { NetworkConnectionInformationService } from 'src/app/services/network/network-connection-information.service';
import { ApplicationDefaults } from 'src/app/services/app/defaults';
import { SnackbarService } from 'src/app/services/notifications/snackbar.service';

@Component({
selector: 'app-web-ui-settings',
Expand Down Expand Up @@ -36,7 +37,7 @@ export class WebUiSettingsComponent implements OnInit {

private web_ui_options: WebUISettings;

constructor(private appConfig: ApplicationConfigService, private auth_service: AuthService) { this.theme_options = ApplicationConfigService.THEME_OPTIONS }
constructor(private appConfig: ApplicationConfigService, private auth_service: AuthService, private snackbar: SnackbarService) { this.theme_options = ApplicationConfigService.THEME_OPTIONS }

ngOnInit(): void {
this.web_ui_options = this.appConfig.getWebUISettings();
Expand Down Expand Up @@ -67,6 +68,23 @@ export class WebUiSettingsComponent implements OnInit {
}
}

registerMagnetHandler() {
if(location.protocol !== 'https:') {
this.snackbar.enqueueSnackBar('HTTPS is required to register magnet URLs!', { type: 'error' });
return;
}

if(typeof navigator.registerProtocolHandler !== 'function') {
this.snackbar.enqueueSnackBar('Your browser does not support registering magnet URLs.', { type: 'error' });
return;
}

let templateHashString = 'download=%s';
let templateURL = location.origin + location.pathname + `#${templateHashString}`;

navigator.registerProtocolHandler('magnet', templateURL, 'qBittorrent WebUI magnet handler');
}

_resetAllSettings() {
if(confirm('Are you sure you want to RESET ALL WEB UI settings?')) {
// Reset & refresh for changes to take affect
Expand Down
8 changes: 7 additions & 1 deletion src/app/services/notifications/snackbar.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ export class SnackbarService {

// If user chose not to view snackbar notifications, then don't enqueue them and instead log to console
if(!this.appConfig.canViewSnackbarNotification()) {
options?.type === 'error' ? console.error('[Error] ' + message) : console.log(`[Notice] ` + message);
if(options?.type === 'error'){
console.error('[Error] ' + message)
alert('[Error] ' + message)
}
else
console.log(`[Notice] ` + message)

return;
}

Expand Down

0 comments on commit 70adae6

Please sign in to comment.