package qbittorrent import ( "bytes" "context" "encoding/json" "errors" "fmt" "net/http" ) type Application interface { // Version get application version Version(context.Context) (string, error) // WebApiVersion get webapi version WebApiVersion(context.Context) (string, error) // BuildInfo get build info BuildInfo(context.Context) (*BuildInfo, error) // Shutdown exit application Shutdown(context.Context) error // GetPreferences get application preferences GetPreferences(context.Context) (*Preferences, error) // SetPreferences set application preferences SetPreferences(context.Context, *Preferences) error // DefaultSavePath get default save path DefaultSavePath(context.Context) (string, error) } type BuildInfo struct { BitNess int `json:"bitness,omitempty"` Boost string `json:"boost,omitempty"` LibTorrent string `json:"libtorrent,omitempty"` Openssl string `json:"openssl,omitempty"` QT string `json:"qt,omitempty"` Zlib string `json:"zlib,omitempty"` } type Preferences struct { AddToTopOfQueue bool `json:"add_to_top_of_queue,omitempty"` AddTrackers string `json:"add_trackers,omitempty"` AddTrackersEnabled bool `json:"add_trackers_enabled,omitempty"` AltDlLimit int `json:"alt_dl_limit,omitempty"` AltUpLimit int `json:"alt_up_limit,omitempty"` AlternativeWebuiEnabled bool `json:"alternative_webui_enabled,omitempty"` AlternativeWebuiPath string `json:"alternative_webui_path,omitempty"` AnnounceIP string `json:"announce_ip,omitempty"` AnnounceToAllTiers bool `json:"announce_to_all_tiers,omitempty"` AnnounceToAllTrackers bool `json:"announce_to_all_trackers,omitempty"` AnonymousMode bool `json:"anonymous_mode,omitempty"` AsyncIoThreads int `json:"async_io_threads,omitempty"` AutoDeleteMode int `json:"auto_delete_mode,omitempty"` AutoTmmEnabled bool `json:"auto_tmm_enabled,omitempty"` AutorunEnabled bool `json:"autorun_enabled,omitempty"` AutorunOnTorrentAddedEnabled bool `json:"autorun_on_torrent_added_enabled,omitempty"` AutorunOnTorrentAddedProgram string `json:"autorun_on_torrent_added_program,omitempty"` AutorunProgram string `json:"autorun_program,omitempty"` BannedIPs string `json:"banned_IPs,omitempty"` BdecodeDepthLimit int `json:"bdecode_depth_limit,omitempty"` BdecodeTokenLimit int `json:"bdecode_token_limit,omitempty"` BittorrentProtocol int `json:"bittorrent_protocol,omitempty"` BlockPeersOnPrivilegedPorts bool `json:"block_peers_on_privileged_ports,omitempty"` BypassAuthSubnetWhitelist string `json:"bypass_auth_subnet_whitelist,omitempty"` BypassAuthSubnetWhitelistEnabled bool `json:"bypass_auth_subnet_whitelist_enabled,omitempty"` BypassLocalAuth bool `json:"bypass_local_auth,omitempty"` CategoryChangedTmmEnabled bool `json:"category_changed_tmm_enabled,omitempty"` CheckingMemoryUse int `json:"checking_memory_use,omitempty"` ConnectionSpeed int `json:"connection_speed,omitempty"` CurrentInterfaceAddress string `json:"current_interface_address,omitempty"` CurrentInterfaceName string `json:"current_interface_name,omitempty"` CurrentNetworkInterface string `json:"current_network_interface,omitempty"` Dht bool `json:"dht,omitempty"` DiskCache int `json:"disk_cache,omitempty"` DiskCacheTTL int `json:"disk_cache_ttl,omitempty"` DiskIoReadMode int `json:"disk_io_read_mode,omitempty"` DiskIoType int `json:"disk_io_type,omitempty"` DiskIoWriteMode int `json:"disk_io_write_mode,omitempty"` DiskQueueSize int `json:"disk_queue_size,omitempty"` DlLimit int `json:"dl_limit,omitempty"` DontCountSlowTorrents bool `json:"dont_count_slow_torrents,omitempty"` DyndnsDomain string `json:"dyndns_domain,omitempty"` DyndnsEnabled bool `json:"dyndns_enabled,omitempty"` DyndnsPassword string `json:"dyndns_password,omitempty"` DyndnsService int `json:"dyndns_service,omitempty"` DyndnsUsername string `json:"dyndns_username,omitempty"` EmbeddedTrackerPort int `json:"embedded_tracker_port,omitempty"` EmbeddedTrackerPortForwarding bool `json:"embedded_tracker_port_forwarding,omitempty"` EnableCoalesceReadWrite bool `json:"enable_coalesce_read_write,omitempty"` EnableEmbeddedTracker bool `json:"enable_embedded_tracker,omitempty"` EnableMultiConnectionsFromSameIP bool `json:"enable_multi_connections_from_same_ip,omitempty"` EnablePieceExtentAffinity bool `json:"enable_piece_extent_affinity,omitempty"` EnableUploadSuggestions bool `json:"enable_upload_suggestions,omitempty"` Encryption int `json:"encryption,omitempty"` ExcludedFileNames string `json:"excluded_file_names,omitempty"` ExcludedFileNamesEnabled bool `json:"excluded_file_names_enabled,omitempty"` ExportDir string `json:"export_dir,omitempty"` ExportDirFin string `json:"export_dir_fin,omitempty"` FileLogAge int `json:"file_log_age,omitempty"` FileLogAgeType int `json:"file_log_age_type,omitempty"` FileLogBackupEnabled bool `json:"file_log_backup_enabled,omitempty"` FileLogDeleteOld bool `json:"file_log_delete_old,omitempty"` FileLogEnabled bool `json:"file_log_enabled,omitempty"` FileLogMaxSize int `json:"file_log_max_size,omitempty"` FileLogPath string `json:"file_log_path,omitempty"` FilePoolSize int `json:"file_pool_size,omitempty"` HashingThreads int `json:"hashing_threads,omitempty"` I2PAddress string `json:"i2p_address,omitempty"` I2PEnabled bool `json:"i2p_enabled,omitempty"` I2PInboundLength int `json:"i2p_inbound_length,omitempty"` I2PInboundQuantity int `json:"i2p_inbound_quantity,omitempty"` I2PMixedMode bool `json:"i2p_mixed_mode,omitempty"` I2POutboundLength int `json:"i2p_outbound_length,omitempty"` I2POutboundQuantity int `json:"i2p_outbound_quantity,omitempty"` I2PPort int `json:"i2p_port,omitempty"` IdnSupportEnabled bool `json:"idn_support_enabled,omitempty"` IncompleteFilesExt bool `json:"incomplete_files_ext,omitempty"` IPFilterEnabled bool `json:"ip_filter_enabled,omitempty"` IPFilterPath string `json:"ip_filter_path,omitempty"` IPFilterTrackers bool `json:"ip_filter_trackers,omitempty"` LimitLanPeers bool `json:"limit_lan_peers,omitempty"` LimitTCPOverhead bool `json:"limit_tcp_overhead,omitempty"` LimitUtpRate bool `json:"limit_utp_rate,omitempty"` ListenPort int `json:"listen_port,omitempty"` Locale string `json:"locale,omitempty"` Lsd bool `json:"lsd,omitempty"` MailNotificationAuthEnabled bool `json:"mail_notification_auth_enabled,omitempty"` MailNotificationEmail string `json:"mail_notification_email,omitempty"` MailNotificationEnabled bool `json:"mail_notification_enabled,omitempty"` MailNotificationPassword string `json:"mail_notification_password,omitempty"` MailNotificationSender string `json:"mail_notification_sender,omitempty"` MailNotificationSMTP string `json:"mail_notification_smtp,omitempty"` MailNotificationSslEnabled bool `json:"mail_notification_ssl_enabled,omitempty"` MailNotificationUsername string `json:"mail_notification_username,omitempty"` MaxActiveCheckingTorrents int `json:"max_active_checking_torrents,omitempty"` MaxActiveDownloads int `json:"max_active_downloads,omitempty"` MaxActiveTorrents int `json:"max_active_torrents,omitempty"` MaxActiveUploads int `json:"max_active_uploads,omitempty"` MaxConcurrentHTTPAnnounces int `json:"max_concurrent_http_announces,omitempty"` MaxConnec int `json:"max_connec,omitempty"` MaxConnecPerTorrent int `json:"max_connec_per_torrent,omitempty"` MaxInactiveSeedingTime int `json:"max_inactive_seeding_time,omitempty"` MaxInactiveSeedingTimeEnabled bool `json:"max_inactive_seeding_time_enabled,omitempty"` MaxRatio int `json:"max_ratio,omitempty"` MaxRatioAct int `json:"max_ratio_act,omitempty"` MaxRatioEnabled bool `json:"max_ratio_enabled,omitempty"` MaxSeedingTime int `json:"max_seeding_time,omitempty"` MaxSeedingTimeEnabled bool `json:"max_seeding_time_enabled,omitempty"` MaxUploads int `json:"max_uploads,omitempty"` MaxUploadsPerTorrent int `json:"max_uploads_per_torrent,omitempty"` MemoryWorkingSetLimit int `json:"memory_working_set_limit,omitempty"` MergeTrackers bool `json:"merge_trackers,omitempty"` OutgoingPortsMax int `json:"outgoing_ports_max,omitempty"` OutgoingPortsMin int `json:"outgoing_ports_min,omitempty"` PeerTos int `json:"peer_tos,omitempty"` PeerTurnover int `json:"peer_turnover,omitempty"` PeerTurnoverCutoff int `json:"peer_turnover_cutoff,omitempty"` PeerTurnoverInterval int `json:"peer_turnover_interval,omitempty"` PerformanceWarning bool `json:"performance_warning,omitempty"` Pex bool `json:"pex,omitempty"` PreallocateAll bool `json:"preallocate_all,omitempty"` ProxyAuthEnabled bool `json:"proxy_auth_enabled,omitempty"` ProxyBittorrent bool `json:"proxy_bittorrent,omitempty"` ProxyHostnameLookup bool `json:"proxy_hostname_lookup,omitempty"` ProxyIP string `json:"proxy_ip,omitempty"` ProxyMisc bool `json:"proxy_misc,omitempty"` ProxyPassword string `json:"proxy_password,omitempty"` ProxyPeerConnections bool `json:"proxy_peer_connections,omitempty"` ProxyPort int `json:"proxy_port,omitempty"` ProxyRss bool `json:"proxy_rss,omitempty"` ProxyType string `json:"proxy_type,omitempty"` ProxyUsername string `json:"proxy_username,omitempty"` QueueingEnabled bool `json:"queueing_enabled,omitempty"` RandomPort bool `json:"random_port,omitempty"` ReannounceWhenAddressChanged bool `json:"reannounce_when_address_changed,omitempty"` RecheckCompletedTorrents bool `json:"recheck_completed_torrents,omitempty"` RefreshInterval int `json:"refresh_interval,omitempty"` RequestQueueSize int `json:"request_queue_size,omitempty"` ResolvePeerCountries bool `json:"resolve_peer_countries,omitempty"` ResumeDataStorageType string `json:"resume_data_storage_type,omitempty"` RssAutoDownloadingEnabled bool `json:"rss_auto_downloading_enabled,omitempty"` RssDownloadRepackProperEpisodes bool `json:"rss_download_repack_proper_episodes,omitempty"` RssMaxArticlesPerFeed int `json:"rss_max_articles_per_feed,omitempty"` RssProcessingEnabled bool `json:"rss_processing_enabled,omitempty"` RssRefreshInterval int `json:"rss_refresh_interval,omitempty"` RssSmartEpisodeFilters string `json:"rss_smart_episode_filters,omitempty"` SavePath string `json:"save_path,omitempty"` SavePathChangedTmmEnabled bool `json:"save_path_changed_tmm_enabled,omitempty"` SaveResumeDataInterval int `json:"save_resume_data_interval,omitempty"` ScheduleFromHour int `json:"schedule_from_hour,omitempty"` ScheduleFromMin int `json:"schedule_from_min,omitempty"` ScheduleToHour int `json:"schedule_to_hour,omitempty"` ScheduleToMin int `json:"schedule_to_min,omitempty"` SchedulerDays int `json:"scheduler_days,omitempty"` SchedulerEnabled bool `json:"scheduler_enabled,omitempty"` SendBufferLowWatermark int `json:"send_buffer_low_watermark,omitempty"` SendBufferWatermark int `json:"send_buffer_watermark,omitempty"` SendBufferWatermarkFactor int `json:"send_buffer_watermark_factor,omitempty"` SlowTorrentDlRateThreshold int `json:"slow_torrent_dl_rate_threshold,omitempty"` SlowTorrentInactiveTimer int `json:"slow_torrent_inactive_timer,omitempty"` SlowTorrentUlRateThreshold int `json:"slow_torrent_ul_rate_threshold,omitempty"` SocketBacklogSize int `json:"socket_backlog_size,omitempty"` SocketReceiveBufferSize int `json:"socket_receive_buffer_size,omitempty"` SocketSendBufferSize int `json:"socket_send_buffer_size,omitempty"` SsrfMitigation bool `json:"ssrf_mitigation,omitempty"` StartPausedEnabled bool `json:"start_paused_enabled,omitempty"` StopTrackerTimeout int `json:"stop_tracker_timeout,omitempty"` TempPath string `json:"temp_path,omitempty"` TempPathEnabled bool `json:"temp_path_enabled,omitempty"` TorrentChangedTmmEnabled bool `json:"torrent_changed_tmm_enabled,omitempty"` TorrentContentLayout string `json:"torrent_content_layout,omitempty"` TorrentFileSizeLimit int `json:"torrent_file_size_limit,omitempty"` TorrentStopCondition string `json:"torrent_stop_condition,omitempty"` UpLimit int `json:"up_limit,omitempty"` UploadChokingAlgorithm int `json:"upload_choking_algorithm,omitempty"` UploadSlotsBehavior int `json:"upload_slots_behavior,omitempty"` Upnp bool `json:"upnp,omitempty"` UpnpLeaseDuration int `json:"upnp_lease_duration,omitempty"` UseCategoryPathsInManualMode bool `json:"use_category_paths_in_manual_mode,omitempty"` UseHTTPS bool `json:"use_https,omitempty"` UseSubcategories bool `json:"use_subcategories,omitempty"` UtpTCPMixedMode int `json:"utp_tcp_mixed_mode,omitempty"` ValidateHTTPSTrackerCertificate bool `json:"validate_https_tracker_certificate,omitempty"` WebUIAddress string `json:"web_ui_address,omitempty"` WebUIBanDuration int `json:"web_ui_ban_duration,omitempty"` WebUIClickjackingProtectionEnabled bool `json:"web_ui_clickjacking_protection_enabled,omitempty"` WebUICsrfProtectionEnabled bool `json:"web_ui_csrf_protection_enabled,omitempty"` WebUICustomHTTPHeaders string `json:"web_ui_custom_http_headers,omitempty"` WebUIDomainList string `json:"web_ui_domain_list,omitempty"` WebUIHostHeaderValidationEnabled bool `json:"web_ui_host_header_validation_enabled,omitempty"` WebUIHTTPSCertPath string `json:"web_ui_https_cert_path,omitempty"` WebUIHTTPSKeyPath string `json:"web_ui_https_key_path,omitempty"` WebUIMaxAuthFailCount int `json:"web_ui_max_auth_fail_count,omitempty"` WebUIPort int `json:"web_ui_port,omitempty"` WebUIReverseProxiesList string `json:"web_ui_reverse_proxies_list,omitempty"` WebUIReverseProxyEnabled bool `json:"web_ui_reverse_proxy_enabled,omitempty"` WebUISecureCookieEnabled bool `json:"web_ui_secure_cookie_enabled,omitempty"` WebUISessionTimeout int `json:"web_ui_session_timeout,omitempty"` WebUIUpnp bool `json:"web_ui_upnp,omitempty"` WebUIUseCustomHTTPHeadersEnabled bool `json:"web_ui_use_custom_http_headers_enabled,omitempty"` WebUIUsername string `json:"web_ui_username,omitempty"` } func (c *client) Version(ctx context.Context) (string, error) { ctx, span := trace.Start(ctx, "qbittorrent.Application.Version") defer span.End() apiUrl := fmt.Sprintf("%s/api/v2/app/version", c.config.Address) result, err := c.doRequest(ctx, &requestData{ url: apiUrl, }) if err != nil { return "", err } if result.code != 200 { return "", errors.New("get version failed: " + string(result.body)) } return string(result.body), nil } func (c *client) WebApiVersion(ctx context.Context) (string, error) { ctx, span := trace.Start(ctx, "qbittorrent.Application.WebApiVersion") defer span.End() apiUrl := fmt.Sprintf("%s/api/v2/app/webapiVersion", c.config.Address) result, err := c.doRequest(ctx, &requestData{ url: apiUrl, }) if err != nil { return "", err } if result.code != 200 { return "", errors.New("get version failed: " + string(result.body)) } return string(result.body), nil } func (c *client) BuildInfo(ctx context.Context) (*BuildInfo, error) { ctx, span := trace.Start(ctx, "qbittorrent.Application.BuildInfo") defer span.End() apiUrl := fmt.Sprintf("%s/api/v2/app/buildInfo", c.config.Address) result, err := c.doRequest(ctx, &requestData{ url: apiUrl, }) if err != nil { return nil, err } if result.code != 200 { return nil, errors.New("get build info failed: " + string(result.body)) } var build = new(BuildInfo) if err := json.Unmarshal(result.body, build); err != nil { return nil, err } return build, nil } func (c *client) Shutdown(ctx context.Context) error { ctx, span := trace.Start(ctx, "qbittorrent.Application.Shutdown") defer span.End() apiUrl := fmt.Sprintf("%s/api/v2/app/shutdown", c.config.Address) result, err := c.doRequest(ctx, &requestData{ method: http.MethodPost, url: apiUrl, }) if err != nil { return err } if result.code != 200 { return errors.New("shutdown application failed: " + string(result.body)) } return nil } func (c *client) GetPreferences(ctx context.Context) (*Preferences, error) { ctx, span := trace.Start(ctx, "qbittorrent.Application.GetPreferences") defer span.End() apiUrl := fmt.Sprintf("%s/api/v2/app/preferences", c.config.Address) result, err := c.doRequest(ctx, &requestData{ url: apiUrl, }) if err != nil { return nil, err } if result.code != 200 { return nil, errors.New("get preference failed: " + string(result.body)) } var preferences = new(Preferences) if err := json.Unmarshal(result.body, preferences); err != nil { return nil, err } return preferences, nil } func (c *client) SetPreferences(ctx context.Context, prefs *Preferences) error { ctx, span := trace.Start(ctx, "qbittorrent.Application.SetPreferences") defer span.End() apiUrl := fmt.Sprintf("%s/api/v2/app/setPreferences", c.config.Address) data, err := json.Marshal(prefs) if err != nil { return err } var formData bytes.Buffer formData.Write([]byte("json=")) formData.Write(data) result, err := c.doRequest(ctx, &requestData{ method: http.MethodPost, url: apiUrl, contentType: ContentTypeFormUrlEncoded, body: &formData, }) if err != nil { return err } if result.code != 200 { return errors.New("set preference failed: " + string(result.body)) } return nil } func (c *client) DefaultSavePath(ctx context.Context) (string, error) { ctx, span := trace.Start(ctx, "qbittorrent.Application.DefaultSavePath") defer span.End() apiUrl := fmt.Sprintf("%s/api/v2/app/defaultSavePath", c.config.Address) result, err := c.doRequest(ctx, &requestData{ url: apiUrl, }) if err != nil { return "", err } if result.code != 200 { return "", errors.New("get default save path failed: " + string(result.body)) } return string(result.body), nil }