Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.0
18 changes: 9 additions & 9 deletions AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let testURL = NSURL(string: "https://www.youtube.com/watch?v=swZJwZeMesk")!
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let testURL = URL(string: "https://www.youtube.com/watch?v=swZJwZeMesk")!
Youtube.h264videosWithYoutubeURL(testURL) { (videoInfo, error) -> Void in
if let videoURLString = videoInfo?["url"] as? String,
videoTitle = videoInfo?["title"] as? String {
let videoTitle = videoInfo?["title"] as? String {
print("\(videoTitle)")
print("\(videoURLString)")
}
}
return true
}

func applicationWillResignActive(application: UIApplication) { }
func applicationWillResignActive(_ application: UIApplication) { }

func applicationDidEnterBackground(application: UIApplication) { }
func applicationDidEnterBackground(_ application: UIApplication) { }

func applicationWillEnterForeground(application: UIApplication) { }
func applicationWillEnterForeground(_ application: UIApplication) { }

func applicationDidBecomeActive(application: UIApplication) { }
func applicationDidBecomeActive(_ application: UIApplication) { }

func applicationWillTerminate(application: UIApplication) { }
func applicationWillTerminate(_ application: UIApplication) { }
}
2 changes: 1 addition & 1 deletion YoutubeSourceParserKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

Pod::Spec.new do |s|
s.name = "YoutubeSourceParserKit"
s.version = "0.2.4"
s.version = "0.2.5"
s.summary = "YouTube Video Link Parser for Swift"
s.homepage = "https://github.com/movielala/YoutubeSourceParserKit"
s.license = "MIT"
Expand Down
23 changes: 20 additions & 3 deletions YoutubeSourceParserKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
ACCC282F1D80B14600BE509E /* Youtube.swift in Sources */ = {isa = PBXBuildFile; fileRef = B24965D71C3397960050DF64 /* Youtube.swift */; };
B24965DC1C3397960050DF64 /* Youtube.swift in Sources */ = {isa = PBXBuildFile; fileRef = B24965D71C3397960050DF64 /* Youtube.swift */; };
B24965E01C33979F0050DF64 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B24965DF1C33979F0050DF64 /* Images.xcassets */; };
B24965E21C3397D70050DF64 /* YoutubeSourceParserKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B24965DA1C3397960050DF64 /* YoutubeSourceParserKitTests.swift */; };
Expand Down Expand Up @@ -139,14 +140,16 @@
attributes = {
LastSwiftMigration = 0710;
LastSwiftUpdateCheck = 0710;
LastUpgradeCheck = 0710;
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = "Toygar Dündaralp";
TargetAttributes = {
C3FC20CC1B4996150000E818 = {
CreatedOnToolsVersion = 6.4;
LastSwiftMigration = 0820;
};
C3FC20E11B4996150000E818 = {
CreatedOnToolsVersion = 6.4;
LastSwiftMigration = 0820;
TestTargetID = C3FC20CC1B4996150000E818;
};
};
Expand Down Expand Up @@ -204,6 +207,7 @@
buildActionMask = 2147483647;
files = (
B24965E21C3397D70050DF64 /* YoutubeSourceParserKitTests.swift in Sources */,
ACCC282F1D80B14600BE509E /* Youtube.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -231,8 +235,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
Expand Down Expand Up @@ -277,8 +283,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
Expand All @@ -297,6 +305,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
Expand All @@ -307,11 +316,13 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
INFOPLIST_FILE = "$(SRCROOT)/YoutubeSourceParserKit/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.dundaralp.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = YoutubeSourceParserKit;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -320,27 +331,31 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
INFOPLIST_FILE = "$(SRCROOT)/YoutubeSourceParserKit/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.dundaralp.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = YoutubeSourceParserKit;
SWIFT_VERSION = 3.0;
};
name = Release;
};
C3FC20F01B4996150000E818 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
FRAMEWORK_SEARCH_PATHS = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "youtube-parserTests/Info.plist";
INFOPLIST_FILE = YoutubeSourceParserKitTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.dundaralp.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = YoutubeSourceParserKit;
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YoutubeSourceParserKit.app/YoutubeSourceParserKit";
};
name = Debug;
Expand All @@ -349,11 +364,13 @@
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
FRAMEWORK_SEARCH_PATHS = "";
INFOPLIST_FILE = "youtube-parserTests/Info.plist";
INFOPLIST_FILE = YoutubeSourceParserKitTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.dundaralp.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = YoutubeSourceParserKit;
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YoutubeSourceParserKit.app/YoutubeSourceParserKit";
};
name = Release;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
Expand Down Expand Up @@ -30,6 +40,16 @@
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
Expand Down
107 changes: 54 additions & 53 deletions YoutubeSourceParserKit/Youtube.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import UIKit

public extension NSURL {
public extension URL {
/**
Parses a query string of an NSURL

Expand All @@ -20,10 +20,11 @@ public extension NSURL {
}

// Note: find youtube ID in m.youtube.com "https://m.youtube.com/#/watch?v=1hZ98an9wjo"
let result = absoluteString.componentsSeparatedByString("?")
let result = self.absoluteString.components(separatedBy: "?")
if result.count > 1 {
return result.last?.dictionaryFromQueryStringComponents()
}

return nil
}
}
Expand All @@ -33,8 +34,8 @@ public extension NSString {
Convenient method for decoding a html encoded string
*/
func stringByDecodingURLFormat() -> String {
let result = self.stringByReplacingOccurrencesOfString("+", withString:" ")
return result.stringByRemovingPercentEncoding!
let result = self.replacingOccurrences(of: "+", with:" ")
return result.removingPercentEncoding ?? ""
}

/**
Expand All @@ -44,20 +45,20 @@ public extension NSString {
*/
func dictionaryFromQueryStringComponents() -> [String: AnyObject] {
var parameters = [String: AnyObject]()
for keyValue in componentsSeparatedByString("&") {
let keyValueArray = keyValue.componentsSeparatedByString("=")
for keyValue in components(separatedBy: "&") {
let keyValueArray = keyValue.components(separatedBy: "=")
if keyValueArray.count < 2 {
continue
}
let key = keyValueArray[0].stringByDecodingURLFormat()
let value = keyValueArray[1].stringByDecodingURLFormat()
parameters[key] = value
parameters[key] = value as AnyObject?
}
return parameters
}
}

public class Youtube: NSObject {
open class Youtube: NSObject {
static let infoURL = "http://www.youtube.com/get_video_info?video_id="
static var userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4"
/**
Expand All @@ -66,23 +67,22 @@ public class Youtube: NSObject {
@param youtubeURL the the complete youtube video url, either youtu.be or youtube.com
@return string with desired youtube id
*/
public static func youtubeIDFromYoutubeURL(youtubeURL: NSURL) -> String? {
if let
youtubeHost = youtubeURL.host,
youtubePathComponents = youtubeURL.pathComponents {
let youtubeAbsoluteString = youtubeURL.absoluteString
if youtubeHost == "youtu.be" as String? {
return youtubePathComponents[1]
} else if youtubeAbsoluteString.rangeOfString("www.youtube.com/embed") != nil {
return youtubePathComponents[2]
} else if youtubeHost == "youtube.googleapis.com" ||
youtubeURL.pathComponents!.first == "www.youtube.com" as String? {
return youtubePathComponents[2]
} else if let
queryString = youtubeURL.dictionaryForQueryString(),
searchParam = queryString["v"] as? String {
return searchParam
}
open static func youtubeIDFromYoutubeURL(_ youtubeURL: URL) -> String? {
if let youtubeHost = youtubeURL.host {
let youtubePathComponents = youtubeURL.pathComponents
let absoluteString = youtubeURL.absoluteString
if youtubeHost == "youtu.be" as String? {
return youtubePathComponents[1]
} else if absoluteString.range(of: "www.youtube.com/embed") != nil {
return youtubePathComponents[2]
} else if youtubeHost == "youtube.googleapis.com" ||
youtubeURL.pathComponents.first == "www.youtube.com" as String? {
return youtubePathComponents[2]
} else if let
queryString = youtubeURL.dictionaryForQueryString(),
let searchParam = queryString["v"] as? String {
return searchParam
}
}
return nil
}
Expand All @@ -93,24 +93,25 @@ public class Youtube: NSObject {
@return dictionary with the available formats for the selected video

*/
public static func h264videosWithYoutubeID(youtubeID: String) -> [String: AnyObject]? {
open static func h264videosWithYoutubeID(_ youtubeID: String) -> [String: AnyObject]? {
let urlString = String(format: "%@%@", infoURL, youtubeID) as String
let url = NSURL(string: urlString)!
let request = NSMutableURLRequest(URL: url)
let url = URL(string: urlString)!
let request: NSMutableURLRequest = NSMutableURLRequest(url: url)
request.timeoutInterval = 5.0
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
request.HTTPMethod = "GET"
request.httpMethod = "GET"
var responseString = NSString()
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let group = dispatch_group_create()
dispatch_group_enter(group)
session.dataTaskWithRequest(request, completionHandler: { (data, response, _) -> Void in
if let data = data as NSData? {
responseString = NSString(data: data, encoding: NSUTF8StringEncoding)!
let session = URLSession(configuration: URLSessionConfiguration.default)
let group = DispatchGroup()
group.enter()
session.dataTask(with: request as URLRequest, completionHandler: { (data, response, _) -> Void in
if let data = data as Data? {
let encoding = String.Encoding.utf8
responseString = NSString(data: data, encoding: encoding.rawValue)!
}
dispatch_group_leave(group)
group.leave()
}).resume()
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
let _ = group.wait(timeout: DispatchTime.distantFuture)
let parts = responseString.dictionaryFromQueryStringComponents()
if parts.count > 0 {
var videoTitle: String = ""
Expand All @@ -126,18 +127,18 @@ public class Youtube: NSObject {
if let _: AnyObject = parts["live_playback"]{
if let hlsvp = parts["hlsvp"] as? String {
return [
"url": "\(hlsvp)",
"title": "\(videoTitle)",
"image": "\(streamImage)",
"isStream": true
"url": "\(hlsvp)" as AnyObject,
"title": "\(videoTitle)" as AnyObject,
"image": "\(streamImage)" as AnyObject,
"isStream": true as AnyObject
]
}
} else {
let fmtStreamMapArray = fmtStreamMap.componentsSeparatedByString(",")
let fmtStreamMapArray = fmtStreamMap.components(separatedBy: ",")
for videoEncodedString in fmtStreamMapArray {
var videoComponents = videoEncodedString.dictionaryFromQueryStringComponents()
videoComponents["title"] = videoTitle
videoComponents["isStream"] = false
videoComponents["title"] = videoTitle as AnyObject?
videoComponents["isStream"] = false as AnyObject?
return videoComponents as [String: AnyObject]
}
}
Expand All @@ -153,17 +154,17 @@ public class Youtube: NSObject {
@param completeBlock the block which is called on completion

*/
public static func h264videosWithYoutubeURL(youtubeURL: NSURL,completion: ((
videoInfo: [String: AnyObject]?, error: NSError?) -> Void)?) {
let priority = DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_async(dispatch_get_global_queue(priority, 0)) {
if let youtubeID = self.youtubeIDFromYoutubeURL(youtubeURL), videoInformation = self.h264videosWithYoutubeID(youtubeID) {
dispatch_async(dispatch_get_main_queue()) {
completion?(videoInfo: videoInformation, error: nil)
open static func h264videosWithYoutubeURL(_ youtubeURL: URL,completion: ((
_ videoInfo: [String: AnyObject]?, _ error: NSError?) -> Void)?) {
let priority = DispatchQoS.QoSClass.background
DispatchQueue.global(qos: priority).async {
if let youtubeID = self.youtubeIDFromYoutubeURL(youtubeURL), let videoInformation = self.h264videosWithYoutubeID(youtubeID) {
DispatchQueue.main.async {
completion?(videoInformation, nil)
}
}else{
dispatch_async(dispatch_get_main_queue()) {
completion?(videoInfo: nil, error: NSError(domain: "com.player.youtube.backgroundqueue", code: 1001, userInfo: ["error": "Invalid YouTube URL"]))
DispatchQueue.main.async {
completion?(nil, NSError(domain: "com.player.youtube.backgroundqueue", code: 1001, userInfo: ["error": "Invalid YouTube URL"]))
}
}
}
Expand Down
Loading