Skip to content

Commit

Permalink
Fix qBittorrent 5.0.0 API
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael-128 committed Jan 9, 2025
1 parent 3230418 commit 9528ef8
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 16 deletions.
8 changes: 6 additions & 2 deletions qBitControl.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
90AE893D2CDBEF1C000E0276 /* RSSArticleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90AE893C2CDBEF19000E0276 /* RSSArticleView.swift */; };
90BA88552B2206F100C8A342 /* LocalNetworkPermissionClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90BA88542B2206F100C8A342 /* LocalNetworkPermissionClass.swift */; };
90DCA7E52B51C393008A9C1B /* ServersHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90DCA7E42B51C393008A9C1B /* ServersHelper.swift */; };
90E1D0542D30438F00B81F12 /* Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90E1D0532D30438C00B81F12 /* Version.swift */; };
90E86FFE2C81C89A00F4EA01 /* qBitDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90E86FFD2C81C89A00F4EA01 /* qBitDataClass.swift */; };
90EF6A492909267A001E9E7F /* AuthClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90EF6A482909267A001E9E7F /* AuthClass.swift */; };
90EF6A4D29093142001E9E7F /* qBitRequestClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90EF6A4C29093142001E9E7F /* qBitRequestClass.swift */; };
Expand Down Expand Up @@ -170,6 +171,7 @@
90BA88532B22019900C8A342 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
90BA88542B2206F100C8A342 /* LocalNetworkPermissionClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNetworkPermissionClass.swift; sourceTree = "<group>"; };
90DCA7E42B51C393008A9C1B /* ServersHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersHelper.swift; sourceTree = "<group>"; };
90E1D0532D30438C00B81F12 /* Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Version.swift; sourceTree = "<group>"; };
90E86FFD2C81C89A00F4EA01 /* qBitDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = qBitDataClass.swift; sourceTree = "<group>"; };
90EF6A482909267A001E9E7F /* AuthClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthClass.swift; sourceTree = "<group>"; };
90EF6A4C29093142001E9E7F /* qBitRequestClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = qBitRequestClass.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -248,6 +250,7 @@
90726B1E2CDA22160032993E /* Models */ = {
isa = PBXGroup;
children = (
90E1D0532D30438C00B81F12 /* Version.swift */,
90726B0C2CDA22160032993E /* AlertIdentifier.swift */,
90726B0E2CDA22160032993E /* Category.swift */,
90726B0F2CDA22160032993E /* File.swift */,
Expand Down Expand Up @@ -567,6 +570,7 @@
907023302CDA5A56007B2199 /* TorrentListViewModel.swift in Sources */,
90BA88552B2206F100C8A342 /* LocalNetworkPermissionClass.swift in Sources */,
90AE89392CDBEC4D000E0276 /* RSSNodeView.swift in Sources */,
90E1D0542D30438F00B81F12 /* Version.swift in Sources */,
90FF93472C8707BF001CDCE4 /* AppInfo.swift in Sources */,
90DCA7E52B51C393008A9C1B /* ServersHelper.swift in Sources */,
90EF6A492909267A001E9E7F /* AuthClass.swift in Sources */,
Expand Down Expand Up @@ -788,7 +792,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = qBitControl/qBitControl.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 10;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 626XV358Y5;
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -824,7 +828,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = qBitControl/qBitControl.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 10;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 626XV358Y5;
ENABLE_PREVIEWS = YES;
Expand Down
2 changes: 2 additions & 0 deletions qBitControl/Classes/ServersHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class ServersHelper: ObservableObject {

if(success) {
self.setActiveServer(id: server.id)
qBittorrent.initialize()
self.isLoggedIn = true
}

Expand All @@ -134,6 +135,7 @@ class ServersHelper: ObservableObject {
DispatchQueue.main.async {
if(success) {
self.setActiveServer(id: server.id)
qBittorrent.initialize()
self.isLoggedIn = true
}

Expand Down
14 changes: 14 additions & 0 deletions qBitControl/Classes/qBitRequestClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,20 @@ class qBitRequest {
}.resume()
}

static func requestVersion(request: URLRequest, completionHandler: @escaping (Version) -> Void) {
URLSession.shared.dataTask(with: request) {
data, response, error in
if let data = data, let versionData = String(data: data, encoding: .utf8) {
let versionString = versionData.filter { "0123456789.".contains($0) }
let versionParts = versionString.split(separator: ".").map { Int($0) ?? 0 }
if versionParts.count >= 3 {
let versionModel = Version(major: versionParts[0], minor: versionParts[1], patch: versionParts[2])
completionHandler(versionModel)
}
}
}.resume()
}

static func requestTagsJSON(request: URLRequest, completionHandler: @escaping ([String]) -> Void) {
URLSession.shared.dataTask(with: request) {
data, response, error in
Expand Down
46 changes: 37 additions & 9 deletions qBitControl/Classes/qBittorrentClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ class qBittorrent {
static private var cookie = "n/a"
static private var url = "http://0.0.0.0"
static private var preferences: qBitPreferences?
static private var version: Version = Version(major: 0, minor: 0, patch: 0)

static func initialize() {
self.fetchVersion()
}

static func savePreferences() {
if(!isCookie()) { return; }
Expand Down Expand Up @@ -50,6 +55,24 @@ class qBittorrent {
return false
}

static private func setVersion(version: Version) {
self.version = version
}

static func fetchVersion() {
let path = "/api/v2/app/version"

let request = qBitRequest.prepareURLRequest(path: path)

qBitRequest.requestVersion(request: request, completionHandler: { version in
Self.setVersion(version: version)
})
}

static func getVersion() -> Version {
return self.version
}

static func getState(state: String) -> String {
switch state {
case "error":
Expand All @@ -58,7 +81,7 @@ class qBittorrent {
return "Missing Files"
case "uploading":
return "Seeding"
case "pausedUP":
case "pausedUP", "stoppedUP":
return "Paused"
case "queuedUP":
return "Queued"
Expand All @@ -74,7 +97,7 @@ class qBittorrent {
return "Downloading"
case "metaDL":
return "Downloading"
case "pausedDL":
case "pausedDL", "stoppedDL":
return "Paused"
case "queuedDL":
return "Queued"
Expand Down Expand Up @@ -111,7 +134,7 @@ class qBittorrent {
return errorIcon
case "uploading":
return uploadIcon
case "pausedUP":
case "pausedUP", "stoppedUP":
return pauseIcon
case "queuedUP":
return queuedIcon
Expand All @@ -127,7 +150,7 @@ class qBittorrent {
return downloadIcon
case "metaDL":
return metadataDownloadIcon
case "pausedDL":
case "pausedDL", "stoppedDL":
return pauseIcon
case "queuedDL":
return queuedIcon
Expand Down Expand Up @@ -163,7 +186,7 @@ class qBittorrent {
return errorColor
case "uploading":
return seedingColor
case "pausedUP":
case "pausedUP", "stoppedUP":
return pausedColor
case "queuedUP":
return pausedColor
Expand All @@ -179,7 +202,7 @@ class qBittorrent {
return downloadingColor
case "metaDL":
return downloadingColor
case "pausedDL":
case "pausedDL", "stoppedDL":
return pausedColor
case "queuedDL":
return pausedColor
Expand Down Expand Up @@ -278,7 +301,9 @@ class qBittorrent {
}

static func pauseTorrent(hash: String) {
let path = "/api/v2/torrents/pause"
// qBittorrent 5.0.0 changes pause route to stop and resume to start
let suffix = self.version.major == 5 ? "stop" : "pause"
let path = "/api/v2/torrents/\(suffix)"

let request = qBitRequest.prepareURLRequest(path: path, queryItems: [URLQueryItem(name: "hashes", value: hash)])

Expand All @@ -298,7 +323,9 @@ class qBittorrent {
}

static func resumeTorrent(hash: String) {
let path = "/api/v2/torrents/resume"
// qBittorrent 5.0.0 changes pause route to stop and resume to start
let suffix = self.version.major == 5 ? "start" : "resume"
let path = "/api/v2/torrents/\(suffix)"

let request = qBitRequest.prepareURLRequest(path: path, queryItems: [URLQueryItem(name: "hashes", value: hash)])

Expand Down Expand Up @@ -635,7 +662,8 @@ class qBittorrent {
}

if paused {
createSetting(name: "paused", value: paused)
if(self.version.major == 5) { createSetting(name: "stopped", value: paused) }
else { createSetting(name: "paused", value: paused) }
}

if dlLimit > 0 {
Expand Down
6 changes: 6 additions & 0 deletions qBitControl/Models/Version.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
struct Version {
let major: Int
let minor: Int
let patch: Int
}

13 changes: 9 additions & 4 deletions qBitControl/ViewModels/TorrentView/TorrentDetailsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class TorrentDetailsViewModel: ObservableObject {
self.torrent = torrent
self.isSequentialDownload = torrent.seq_dl
self.isFLPiecesFirst = torrent.f_l_piece_prio
self.fetchState(state: torrent.state)
}

func setRefreshTimer() {
Expand All @@ -39,15 +40,19 @@ class TorrentDetailsViewModel: ObservableObject {
self.torrent = torrent
self.isSequentialDownload = torrent.seq_dl
self.isFLPiecesFirst = torrent.f_l_piece_prio

if(torrent.state.contains("paused")) { self.state = .paused }
else if(torrent.state.contains("forced")) { self.state = .forceStart }
else { self.state = .resumed }
self.fetchState(state: torrent.state)
}
}
}
}

private func fetchState(state: String) {
let state = qBittorrent.getState(state: state)
if(state == "Paused") { self.state = .paused }
else if(torrent.state.contains("forced")) { self.state = .forceStart }
else { self.state = .resumed }
}

func getCategory() -> String { torrent.category != "" ? torrent.category : "None" }
func getTags() -> String { torrent.tags != "" ? torrent.tags : "None" }
func getAddedOn() -> String { qBittorrent.getFormatedDate(date: torrent.added_on) }
Expand Down
2 changes: 1 addition & 1 deletion qBitControl/Views/TorrentViews/TorrentListHelperView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ struct TorrentListHelperView: View {
}

func torrentRowManageControls(torrent: Torrent) -> some View {
let isTorrentPaused = torrent.state.contains("paused")
let isTorrentPaused = qBittorrent.getState(state: torrent.state).contains("Paused")

return Section(header: Text("Manage")) {
Button { if isTorrentPaused { qBittorrent.resumeTorrent(hash: torrent.hash) } else { qBittorrent.pauseTorrent(hash: torrent.hash) } }
Expand Down

0 comments on commit 9528ef8

Please sign in to comment.