URLHandler.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*****************************************************************************
  2. * URLHandler.swift
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2018 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Carola Nitz <caro # videolan.org>
  9. *
  10. * Refer to the COPYING file of the official project for license.
  11. *****************************************************************************/
  12. import Foundation
  13. @objc protocol VLCURLHandler {
  14. func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool
  15. func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool
  16. }
  17. @objc class URLHandlers: NSObject {
  18. @objc static let googleURLHandler = GoogleURLHandler()
  19. @objc static let handlers =
  20. [
  21. googleURLHandler,
  22. DropBoxURLHandler(),
  23. FileURLHandler(),
  24. XCallbackURLHandler(),
  25. VLCCallbackURLHandler(),
  26. ElseCallbackURLHandler()
  27. ]
  28. }
  29. class DropBoxURLHandler: NSObject, VLCURLHandler {
  30. @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  31. return url.scheme == "db-a60fc6qj9zdg7bw"
  32. }
  33. @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  34. let authResult = DBClientsManager.handleRedirectURL(url)
  35. if let authResult = authResult, authResult.isSuccess() == true {
  36. //TODO:update Dropboxcontrollers
  37. return true
  38. }
  39. return false
  40. }
  41. }
  42. class GoogleURLHandler: NSObject, VLCURLHandler {
  43. @objc var currentGoogleAuthorizationFlow: OIDAuthorizationFlowSession?
  44. @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  45. return url.scheme == "com.googleusercontent.apps.CLIENT"
  46. }
  47. @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  48. if currentGoogleAuthorizationFlow?.resumeAuthorizationFlow(with: url) == true {
  49. currentGoogleAuthorizationFlow = nil
  50. return true
  51. }
  52. return false
  53. }
  54. }
  55. class FileURLHandler: NSObject, VLCURLHandler {
  56. @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  57. return url.isFileURL
  58. }
  59. @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  60. let subclass = DocumentClass(fileURL: url)
  61. subclass.open { _ in
  62. play(url: url) { _ in
  63. subclass.close(completionHandler: nil)
  64. }
  65. }
  66. return true
  67. }
  68. }
  69. class XCallbackURLHandler: NSObject, VLCURLHandler {
  70. @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  71. return url.scheme == "vlc-x-callback" || url.scheme == "x-callback-url"
  72. }
  73. @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  74. let action = url.path.replacingOccurrences(of: "/", with: "")
  75. var movieURL: URL?
  76. var successCallback: URL?
  77. var errorCallback: URL?
  78. var fileName: String?
  79. guard let query = url.query else {
  80. assertionFailure("no query")
  81. return false
  82. }
  83. for entry in query.components(separatedBy: "&") {
  84. let components = entry.components(separatedBy: "=")
  85. if components.count < 2 {
  86. continue
  87. }
  88. if let key = components.first, let value = components[1].removingPercentEncoding {
  89. if key == "url"{
  90. movieURL = URL(string: value)
  91. } else if key == "filename" {
  92. fileName = value
  93. } else if key == "x-success" {
  94. successCallback = URL(string: value)
  95. } else if key == "x-error" {
  96. errorCallback = URL(string: value)
  97. }
  98. } else {
  99. assertionFailure("no key or app value")
  100. }
  101. }
  102. if action == "stream", let movieURL = movieURL {
  103. play(url: movieURL) { success in
  104. guard let callback = success ? successCallback : errorCallback else {
  105. assertionFailure("no CallbackURL")
  106. return
  107. }
  108. if #available(iOS 10, *) {
  109. UIApplication.shared.open(callback, options: [:], completionHandler: nil)
  110. } else {
  111. UIApplication.shared.openURL(callback)
  112. }
  113. }
  114. return true
  115. } else if action == "download", let movieURL = movieURL {
  116. downloadMovie(from:movieURL, fileNameOfMedia:fileName)
  117. return true
  118. }
  119. return false
  120. }
  121. }
  122. class VLCCallbackURLHandler: NSObject, VLCURLHandler {
  123. @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  124. return url.scheme == "vlc"
  125. }
  126. // Safari fixes URLs like "vlc://http://example.org" to "vlc://http//example.org"
  127. func transformVLCURL(_ url: URL) -> URL {
  128. var parsedString = url.absoluteString.replacingOccurrences(of: "vlc://", with: "")
  129. if let location = parsedString.range(of: "//"), parsedString[parsedString.index(location.lowerBound, offsetBy: -1)] != ":" {
  130. parsedString = "\(parsedString[parsedString.startIndex..<location.lowerBound])://\(parsedString[location.upperBound...])"
  131. } else if !parsedString.hasPrefix("http://") && !parsedString.hasPrefix("https://") && !parsedString.hasPrefix("ftp://") {
  132. parsedString = "http://\(parsedString)"
  133. }
  134. return URL(string: parsedString)!
  135. }
  136. func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  137. let transformedURL = transformVLCURL(url)
  138. let scheme = transformedURL.scheme
  139. if scheme == "http" || scheme == "https" || scheme == "ftp" {
  140. let alert = UIAlertController(title: NSLocalizedString("OPEN_STREAM_OR_DOWNLOAD", comment:""), message: url.absoluteString, preferredStyle: .alert)
  141. let downloadAction = UIAlertAction(title: NSLocalizedString("BUTTON_DOWNLOAD", comment:""), style: .default) { _ in
  142. downloadMovie(from:transformedURL, fileNameOfMedia:nil)
  143. }
  144. alert.addAction(downloadAction)
  145. let playAction = UIAlertAction(title: NSLocalizedString("PLAY_BUTTON", comment:""), style: .default) { _ in
  146. play(url: transformedURL, completion: nil)
  147. }
  148. alert.addAction(playAction)
  149. alert.show(UIApplication.shared.keyWindow!.rootViewController!, sender: nil)
  150. } else {
  151. play(url: transformedURL, completion: nil)
  152. }
  153. return true
  154. }
  155. }
  156. class ElseCallbackURLHandler: NSObject, VLCURLHandler {
  157. @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  158. return true
  159. }
  160. func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
  161. play(url: url, completion: nil)
  162. return true
  163. }
  164. }
  165. // TODO: This code should probably not live here
  166. func play(url: URL, completion: ((Bool) -> Void)?) {
  167. let vpc = VLCPlaybackController.sharedInstance()
  168. vpc.fullscreenSessionRequested = true
  169. if let mediaList = VLCMediaList(array: [VLCMedia(url: url)]) {
  170. vpc.playMediaList(mediaList, firstIndex: 0, subtitlesFilePath: nil, completion: completion)
  171. }
  172. }
  173. func downloadMovie(from url: URL, fileNameOfMedia fileName: String?) {
  174. VLCDownloadViewController.sharedInstance().addURL(toDownloadList: url, fileNameOfMedia: fileName)
  175. }