URLHandler.swift 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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 public protocol VLCURLHandler {
  14. func canHandleOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool
  15. func performOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: 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: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  31. return url.scheme == "db-a60fc6qj9zdg7bw"
  32. }
  33. @objc func performOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: 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: OIDExternalUserAgentSession?
  44. @objc func canHandleOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  45. return url.scheme == "com.googleusercontent.apps.CLIENT"
  46. }
  47. @objc func performOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  48. if currentGoogleAuthorizationFlow?.resumeExternalUserAgentFlow(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: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  57. return url.isFileURL
  58. }
  59. @objc func performOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  60. let subclass = DocumentClass(fileURL: url)
  61. subclass.open { _ in
  62. self.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: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  71. return url.scheme == "vlc-x-callback" || url.scheme == "x-callback-url"
  72. }
  73. @objc func performOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  74. let action = url.path.replacingOccurrences(of: "/", with: "")
  75. var movieURL: URL?
  76. var subURL: URL?
  77. var successCallback: URL?
  78. var errorCallback: URL?
  79. var fileName: String?
  80. guard let query = url.query else {
  81. assertionFailure("no query")
  82. return false
  83. }
  84. for entry in query.components(separatedBy: "&") {
  85. let components = entry.components(separatedBy: "=")
  86. if components.count < 2 {
  87. continue
  88. }
  89. if let key = components.first, let value = components[1].removingPercentEncoding {
  90. if key == "url"{
  91. movieURL = URL(string: value)
  92. } else if key == "sub" {
  93. subURL = URL(string: value)
  94. } else if key == "filename" {
  95. fileName = value
  96. } else if key == "x-success" {
  97. successCallback = URL(string: value)
  98. } else if key == "x-error" {
  99. errorCallback = URL(string: value)
  100. }
  101. } else {
  102. assertionFailure("no key or app value")
  103. }
  104. }
  105. if action == "stream", let movieURL = movieURL {
  106. play(url: movieURL, sub: subURL) { success in
  107. guard let callback = success ? successCallback : errorCallback else {
  108. assertionFailure("no CallbackURL")
  109. return
  110. }
  111. if #available(iOS 10, *) {
  112. UIApplication.shared.open(callback, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
  113. } else {
  114. UIApplication.shared.openURL(callback)
  115. }
  116. }
  117. return true
  118. } else if action == "download", let movieURL = movieURL {
  119. downloadMovie(from:movieURL, fileNameOfMedia:fileName)
  120. return true
  121. }
  122. return false
  123. }
  124. }
  125. public class VLCCallbackURLHandler: NSObject, VLCURLHandler {
  126. @objc public func canHandleOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  127. return url.scheme == "vlc"
  128. }
  129. // Safari fixes URLs like "vlc://http://example.org" to "vlc://http//example.org"
  130. public func transformVLCURL(_ url: URL) -> URL {
  131. var parsedString = url.absoluteString.replacingOccurrences(of: "vlc://", with: "")
  132. if let location = parsedString.range(of: "//"), parsedString[parsedString.index(location.lowerBound, offsetBy: -1)] != ":" {
  133. parsedString = "\(parsedString[parsedString.startIndex..<location.lowerBound])://\(parsedString[location.upperBound...])"
  134. } else if !parsedString.hasPrefix("http://") && !parsedString.hasPrefix("https://") && !parsedString.hasPrefix("ftp://") {
  135. parsedString = "http://\(parsedString)"
  136. }
  137. return URL(string: parsedString)!
  138. }
  139. public func performOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  140. let transformedURL = transformVLCURL(url)
  141. let scheme = transformedURL.scheme
  142. if scheme == "http" || scheme == "https" || scheme == "ftp" {
  143. let alert = UIAlertController(title: NSLocalizedString("OPEN_STREAM_OR_DOWNLOAD", comment:""), message: url.absoluteString, preferredStyle: .alert)
  144. let downloadAction = UIAlertAction(title: NSLocalizedString("BUTTON_DOWNLOAD", comment:""), style: .default) { _ in
  145. self.downloadMovie(from:transformedURL, fileNameOfMedia:nil)
  146. }
  147. alert.addAction(downloadAction)
  148. let playAction = UIAlertAction(title: NSLocalizedString("PLAY_BUTTON", comment:""), style: .default) { _ in
  149. self.play(url: transformedURL, completion: nil)
  150. }
  151. alert.addAction(playAction)
  152. alert.show(UIApplication.shared.keyWindow!.rootViewController!, sender: nil)
  153. } else {
  154. self.play(url: transformedURL, completion: nil)
  155. }
  156. return true
  157. }
  158. }
  159. class ElseCallbackURLHandler: NSObject, VLCURLHandler {
  160. @objc func canHandleOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  161. return true
  162. }
  163. func performOpen(url: URL, options: [UIApplication.OpenURLOptionsKey: AnyObject]) -> Bool {
  164. self.play(url: url, completion: nil)
  165. return true
  166. }
  167. }
  168. extension VLCURLHandler {
  169. // TODO: This code should probably not live here
  170. func play(url: URL, sub: URL? = nil, completion: ((Bool) -> Void)?) {
  171. let vpc = VLCPlaybackController.sharedInstance()
  172. vpc.fullscreenSessionRequested = true
  173. if let mediaList = VLCMediaList(array: [VLCMedia(url: url)]) {
  174. vpc.playMediaList(mediaList, firstIndex: 0, subtitlesFilePath: sub?.absoluteString, completion: completion)
  175. }
  176. }
  177. func downloadMovie(from url: URL, fileNameOfMedia fileName: String?) {
  178. VLCDownloadViewController.sharedInstance().addURL(toDownloadList: url, fileNameOfMedia: fileName)
  179. }
  180. }
  181. // Helper function inserted by Swift 4.2 migrator.
  182. fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
  183. return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
  184. }