diff --git a/Pantomime.xcodeproj/project.pbxproj b/Pantomime.xcodeproj/project.pbxproj index 288e5a7..d388640 100755 --- a/Pantomime.xcodeproj/project.pbxproj +++ b/Pantomime.xcodeproj/project.pbxproj @@ -300,15 +300,17 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1330; ORGANIZATIONNAME = "Thomas Christensen"; TargetAttributes = { 9EDCE3E31C09D211002FA4A7 = { CreatedOnToolsVersion = 7.1.1; + DevelopmentTeam = W9ZVWEMJZF; LastSwiftMigration = 1020; }; 9EDCE3ED1C09D211002FA4A7 = { CreatedOnToolsVersion = 7.1.1; + DevelopmentTeam = W9ZVWEMJZF; LastSwiftMigration = 1020; }; ACE597861C18830D0031451F = { @@ -316,9 +318,8 @@ LastSwiftMigration = 0800; }; ACE597971C18832E0031451F = { - DevelopmentTeam = W9ZVWEMJZF; LastSwiftMigration = 0800; - ProvisioningStyle = Automatic; + ProvisioningStyle = Manual; }; }; }; @@ -515,6 +516,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -540,7 +542,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -576,6 +578,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -595,10 +598,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = NO; SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; SWIFT_VERSION = 4.0; VALIDATE_PRODUCT = YES; VALID_ARCHS = "arm64 armv7 armv7s x86_64"; @@ -732,18 +736,18 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = W9ZVWEMJZF; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.nordija.Pantomime; PRODUCT_NAME = Pantomime; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -751,8 +755,7 @@ SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; - VALID_ARCHS = "i386 x86_64"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -760,18 +763,18 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = W9ZVWEMJZF; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.nordija.Pantomime; PRODUCT_NAME = Pantomime; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -779,8 +782,7 @@ SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 4.0; - VALID_ARCHS = "i386 x86_64"; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Pantomime.xcodeproj/xcshareddata/xcschemes/Pantomime (OSX).xcscheme b/Pantomime.xcodeproj/xcshareddata/xcschemes/Pantomime (OSX).xcscheme index 80e2ffa..a652b54 100755 --- a/Pantomime.xcodeproj/xcshareddata/xcschemes/Pantomime (OSX).xcscheme +++ b/Pantomime.xcodeproj/xcshareddata/xcschemes/Pantomime (OSX).xcscheme @@ -1,6 +1,6 @@ - - - - + + + + @@ -39,17 +48,6 @@ - - - - - - - - - - - - Void)?) -> MasterPlaylist { - var masterPlaylist = MasterPlaylist() + let masterPlaylist = MasterPlaylist() var currentMediaPlaylist: MediaPlaylist? - + defer { reader.close() } + while let line = reader.readLine() { if line.isEmpty { // Skip empty lines @@ -36,18 +37,39 @@ open class ManifestBuilder { } else if line.hasPrefix("#EXT-X-STREAM-INF") { // #EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=200000 - currentMediaPlaylist = MediaPlaylist() - do { - let programIdString = try line.replace("(.*)=(\\d+),(.*)", replacement: "$2") - let bandwidthString = try line.replace("(.*),(.*)=(\\d+)(.*)", replacement: "$3") - if let currentMediaPlaylistExist = currentMediaPlaylist { - currentMediaPlaylistExist.programId = Int(programIdString)! - currentMediaPlaylistExist.bandwidth = Int(bandwidthString)! + if let type = parseLine(line, attribute: "TYPE"), let mediaType = MediaType(rawValue: type) { + currentMediaPlaylist = MediaPlaylist(type: mediaType) + } + if let mediaPlaylist = currentMediaPlaylist { + do { + if let brandwidth = parseLine(line, attribute: "BANDWIDTH") { + mediaPlaylist.bandwidth = Int(brandwidth)! + } + + let programIdString = try line.replace("(.*)=(\\d+),(.*)", replacement: "$2") + mediaPlaylist.programId = Int(programIdString)! + + } catch { + print("Failed to parse program-id and bandwidth on master playlist. Line = \(line)") + } + } + } else if line.hasPrefix("#EXT-X-MEDIA:TYPE=SUBTITLES") { + // #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",LANGUAGE="eng",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="subtitles/eng/prog_index.m3u8",FORCED=NO + if let type = parseLine(line, attribute: "TYPE"), let mediaType = MediaType(rawValue: type) { + currentMediaPlaylist = MediaPlaylist(type: mediaType) + } + + if let mediaPlaylist = currentMediaPlaylist { + mediaPlaylist.path = parseLine(line, attribute: "URI")?.replacingOccurrences(of: "\"", with: "") + mediaPlaylist.language = parseLine(line, attribute: "LANGUAGE") + mediaPlaylist.name = parseLine(line, attribute: "NAME") + + mediaPlaylist.masterPlaylist = masterPlaylist + masterPlaylist.addPlaylist(mediaPlaylist) + if let callableOnMediaPlaylist = onMediaPlaylist { + callableOnMediaPlaylist(mediaPlaylist) } - } catch { - print("Failed to parse program-id and bandwidth on master playlist. Line = \(line)") } - } } else if line.hasPrefix("#") { // Comments are ignored @@ -67,6 +89,16 @@ open class ManifestBuilder { return masterPlaylist } + + private func parseLine(_ line: String, attribute: String) -> String? { + let params = line.split(separator: ",") + + if let index = params.firstIndex(where: { $0.trimmingCharacters(in: .whitespaces).contains(attribute)}) { + return params[index].split(separator: "=").map(String.init).last + } + + return nil + } /** * Parses Media Playlist manifests diff --git a/Sources/MasterPlaylist.swift b/Sources/MasterPlaylist.swift index d65ef35..d9f261f 100644 --- a/Sources/MasterPlaylist.swift +++ b/Sources/MasterPlaylist.swift @@ -10,6 +10,8 @@ import Foundation open class MasterPlaylist { var playlists = [MediaPlaylist]() +// var subtitles = [MediaSubtitle]() + open var path: String? public init() {} @@ -28,4 +30,23 @@ open class MasterPlaylist { open func getPlaylistCount() -> Int { return playlists.count } + + open func getPlaylists(type: MediaType) -> [MediaPlaylist] { + return playlists.filter { $0.type == type } + } + +// open func getSubtitleCount() -> Int { +// return subtitles.count +// } +// +// open func addSubtitle(_ subtitle: MediaSubtitle) { +// subtitles.append(subtitle) +// } +// +// open func getSubtitle(_ index: Int) -> MediaSubtitle? { +// if index >= subtitles.count { +// return nil +// } +// return subtitles[index] +// } } diff --git a/Sources/MediaPlaylist.swift b/Sources/MediaPlaylist.swift index e977c50..b39ce09 100644 --- a/Sources/MediaPlaylist.swift +++ b/Sources/MediaPlaylist.swift @@ -5,19 +5,40 @@ import Foundation +public enum MediaType: String { + case unknown = "UNKNOWN" + case audio = "AUDIO" + case video = "VIDEO" + case subtitles = "SUBTITLES" + case closeCaptions = "CLOSED-CAPTIONS" +} + open class MediaPlaylist { var masterPlaylist: MasterPlaylist? - + // EXT-X-STREAM-INF open var programId: Int = 0 open var bandwidth: Int = 0 open var path: String? open var version: Int? open var targetDuration: Int? open var mediaSequence: Int? + + // EXT-X-MEDIA + open var type: MediaType = .unknown // Valid strings are AUDIO, VIDEO, SUBTITLES, and CLOSED-CAPTIONS. This attribute is REQUIRED. + open var groupId: String? + open var name: String? + open var language: String? + open var isDefault: Bool = false + open var isAutoSelect: Bool = false + var segments = [MediaSegment]() + public init(type: MediaType) { + self.type = type + } + public init() { - + } open func addSegment(_ segment: MediaSegment) { @@ -34,6 +55,10 @@ open class MediaPlaylist { open func getSegmentCount() -> Int { return segments.count } + + open func getAllSegments() -> [MediaSegment] { + return segments + } open func duration() -> Float { var dur: Float = 0.0