- Handled the app life cycle for theme 2

- Added Teaser api with data model.
- Handled the tabletitle show hide as per the media type
- Updated hindi lingual file for web series
- Worked on playing a item in playlist
- Woka debugging for analytics
This commit is contained in:
2024-06-25 11:29:00 +05:30
parent ff464359a8
commit 93956e6e98
18 changed files with 630 additions and 106 deletions

View File

@@ -13,7 +13,6 @@
522242662BFC74380085C632 /* MyListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522242632BFC74380085C632 /* MyListVC.swift */; };
522242682BFC74380085C632 /* TabBarVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522242652BFC74380085C632 /* TabBarVC.swift */; };
5222426A2BFC7AFC0085C632 /* SideMenuVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522242692BFC7AFC0085C632 /* SideMenuVC.swift */; };
522A93132C0DB5D50098FE49 /* JWPlayerKit in Frameworks */ = {isa = PBXBuildFile; productRef = 522A93122C0DB5D50098FE49 /* JWPlayerKit */; };
522A931A2C0DE8CC0098FE49 /* SideBarNav.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 522A93192C0DE8CC0098FE49 /* SideBarNav.storyboard */; };
522A931C2C0DE9150098FE49 /* AboutUsVc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522A931B2C0DE9150098FE49 /* AboutUsVc.swift */; };
522D655E2C1ACCF40021E505 /* UserNotificationDM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522D655D2C1ACCF40021E505 /* UserNotificationDM.swift */; };
@@ -89,6 +88,8 @@
52A981D02C1AFEE8000E0BEC /* MyListVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A981CF2C1AFEE8000E0BEC /* MyListVM.swift */; };
52A981D72C1B0E27000E0BEC /* FavouriteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A981D52C1B0E27000E0BEC /* FavouriteCell.swift */; };
52A981D82C1B0E27000E0BEC /* FavouriteCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 52A981D62C1B0E27000E0BEC /* FavouriteCell.xib */; };
52AC2D252C295A7900337473 /* TeaserDM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AC2D242C295A7900337473 /* TeaserDM.swift */; };
52AC2D272C29791500337473 /* JWPlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AC2D262C29791500337473 /* JWPlayerManager.swift */; };
52AECA802C08BCB6004A7579 /* PlayerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AECA7F2C08BCB6004A7579 /* PlayerVC.swift */; };
52B8D4D92C04A25E00ED65F3 /* UIViewController+Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B8D4CE2C04A25D00ED65F3 /* UIViewController+Container.swift */; };
52B8D4DA2C04A25E00ED65F3 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B8D4CF2C04A25D00ED65F3 /* Preferences.swift */; };
@@ -150,6 +151,7 @@
52D774F12BDFC53B001D87DE /* StringSubScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D774F02BDFC53B001D87DE /* StringSubScript.swift */; };
52DAC6482C21762900E2F85B /* WebSeries.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52DAC6472C21762900E2F85B /* WebSeries.storyboard */; };
52DAC64E2C21775300E2F85B /* WebSeriesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52DAC64D2C21775300E2F85B /* WebSeriesVC.swift */; };
52E59A922C29AE3F00BB9B04 /* JWPlayerKit in Frameworks */ = {isa = PBXBuildFile; productRef = 52E59A912C29AE3F00BB9B04 /* JWPlayerKit */; };
52FB2D8F2BDF898F0009B0C7 /* TextFieldPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FB2D8E2BDF898F0009B0C7 /* TextFieldPadding.swift */; };
52FDBA782BFF23F4009D7AC7 /* TimePeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FDBA772BFF23F4009D7AC7 /* TimePeriod.swift */; };
52FDBA7B2BFF2712009D7AC7 /* AuthFuncTimeHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FDBA7A2BFF2712009D7AC7 /* AuthFuncTimeHandling.swift */; };
@@ -340,6 +342,8 @@
52A981CF2C1AFEE8000E0BEC /* MyListVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyListVM.swift; sourceTree = "<group>"; };
52A981D52C1B0E27000E0BEC /* FavouriteCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavouriteCell.swift; sourceTree = "<group>"; };
52A981D62C1B0E27000E0BEC /* FavouriteCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FavouriteCell.xib; sourceTree = "<group>"; };
52AC2D242C295A7900337473 /* TeaserDM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeaserDM.swift; sourceTree = "<group>"; };
52AC2D262C29791500337473 /* JWPlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWPlayerManager.swift; sourceTree = "<group>"; };
52AECA7F2C08BCB6004A7579 /* PlayerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerVC.swift; sourceTree = "<group>"; };
52B8D4CE2C04A25D00ED65F3 /* UIViewController+Container.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Container.swift"; sourceTree = "<group>"; };
52B8D4CF2C04A25D00ED65F3 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
@@ -485,7 +489,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
522A93132C0DB5D50098FE49 /* JWPlayerKit in Frameworks */,
52E59A922C29AE3F00BB9B04 /* JWPlayerKit in Frameworks */,
9C1C69FA2C106B290035B2C7 /* RSKPlaceholderTextView in Frameworks */,
619A5A1BD8BD968ADC83C106 /* Pods_WOKA.framework in Frameworks */,
);
@@ -962,6 +966,7 @@
52DAC64A2C2176CB00E2F85B /* ViewModel */,
52DAC6492C21767900E2F85B /* Controller */,
52DAC6472C21762900E2F85B /* WebSeries.storyboard */,
52AC2D262C29791500337473 /* JWPlayerManager.swift */,
);
path = WebSeries;
sourceTree = "<group>";
@@ -1007,6 +1012,7 @@
52D6A2532C22B93F00145908 /* CategoryListingDM.swift */,
528E5F1A2C24531200E33E4E /* SeasonListingDM.swift */,
9C007F1F2C255DF200F798C2 /* SeasonEpisodeListingDM.swift */,
52AC2D242C295A7900337473 /* TeaserDM.swift */,
);
path = Model;
sourceTree = "<group>";
@@ -1289,8 +1295,8 @@
);
name = WOKA;
packageProductDependencies = (
522A93122C0DB5D50098FE49 /* JWPlayerKit */,
9C1C69F92C106B290035B2C7 /* RSKPlaceholderTextView */,
52E59A912C29AE3F00BB9B04 /* JWPlayerKit */,
);
productName = WOKA;
productReference = 523ED25A2BDA2BC700CFED02 /* WOKA.app */;
@@ -1366,8 +1372,8 @@
);
mainGroup = 523ED2512BDA2BC700CFED02;
packageReferences = (
522A93112C0DB5D50098FE49 /* XCRemoteSwiftPackageReference "JWPlayerKit-package" */,
9C1C69F82C106B290035B2C7 /* XCRemoteSwiftPackageReference "RSKPlaceholderTextView" */,
52E59A902C29AE3F00BB9B04 /* XCRemoteSwiftPackageReference "JWPlayerKit-package" */,
);
productRefGroup = 523ED25B2BDA2BC700CFED02 /* Products */;
projectDirPath = "";
@@ -1577,6 +1583,7 @@
9C56E83B2BDBC6E600E4CA14 /* SelectAgeVM.swift in Sources */,
9C535DC02C00B36000DA6DCD /* HomeVC.swift in Sources */,
52B8D4E22C04A25E00ED65F3 /* Segue.swift in Sources */,
52AC2D272C29791500337473 /* JWPlayerManager.swift in Sources */,
5259542E2BEA393700191286 /* AlertCustomVC.swift in Sources */,
52CA28FA2BE119F500708B49 /* UserIntrestVC.swift in Sources */,
9CBE1B442C0F37B300CA6E61 /* DropDown+Appearance.swift in Sources */,
@@ -1644,6 +1651,7 @@
52C1A4E12C05B69F007BAA50 /* UIApplicationSwitchRoot.swift in Sources */,
52663FF72BDFACF60001D8CE /* ShadowView.swift in Sources */,
522D65602C1ACD8D0021E505 /* UserNotificationVC.swift in Sources */,
52AC2D252C295A7900337473 /* TeaserDM.swift in Sources */,
9C535DC22C00B36900DA6DCD /* ThemeTwoVC.swift in Sources */,
9CA7C6C22C1095B600D73742 /* ProfileVM.swift in Sources */,
52D774F12BDFC53B001D87DE /* StringSubScript.swift in Sources */,
@@ -2074,7 +2082,7 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
522A93112C0DB5D50098FE49 /* XCRemoteSwiftPackageReference "JWPlayerKit-package" */ = {
52E59A902C29AE3F00BB9B04 /* XCRemoteSwiftPackageReference "JWPlayerKit-package" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/jwplayer/JWPlayerKit-package.git";
requirement = {
@@ -2093,9 +2101,9 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
522A93122C0DB5D50098FE49 /* JWPlayerKit */ = {
52E59A912C29AE3F00BB9B04 /* JWPlayerKit */ = {
isa = XCSwiftPackageProductDependency;
package = 522A93112C0DB5D50098FE49 /* XCRemoteSwiftPackageReference "JWPlayerKit-package" */;
package = 52E59A902C29AE3F00BB9B04 /* XCRemoteSwiftPackageReference "JWPlayerKit-package" */;
productName = JWPlayerKit;
};
9C1C69F92C106B290035B2C7 /* RSKPlaceholderTextView */ = {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -89,10 +89,15 @@ class MyListVM{
// Deleting the hindi series from main (those with category ID "18")
for (index, element) in data.enumerated() {
if let bookMarkCatID = element.bookmarkCategoryIDS?.components(separatedBy: ","){
if bookMarkCatID.count > 1{
if bookMarkCatID.count > 1{ // means multiple language
/*
if its greater than one , its for sure we have extracted the hindi episode
*/
hindiData[index].bookmarkCategoryIDS = "1"
}else{
indicesToRemove.append(index)
}else{ // means single language
if bookMarkCatID.first == "18"{
indicesToRemove.append(index)
}
}
}
}

View File

@@ -222,3 +222,6 @@
"Select Video Language" = "वीडियो भाषा चुनें";
"TRAILER" = "ट्रेलर";
"TEASERS" = "टीज़र";
"CONTINUE WATCHING" = "देखना जारी रखें";
"PLAY TRAILER" = "प्ले ट्रेलर";

View File

@@ -21,6 +21,8 @@ struct APIEndPoints {
struct BaseURL {
static let staging = "https://wokaland.com/admin/api/"
static let production = "https://simplitend.com"
static let appUrl = "https://apps.apple.com/in/app/woka/id6465305185"
}
struct Auth {
@@ -85,6 +87,7 @@ struct APIEndPoints {
static let category_listing = makeURL(path: "category_listing")
static let season_listing = makeURL(path: "season_listing")
static let season_episode_listing = makeURL(path: "season_episode_listing")
static let teaser_listing = makeURL(path: "teaser_listing")
}
// Other endpoint categories...

View File

@@ -16,6 +16,7 @@ class PlayerVC: JWPlayerViewController, JWPlayerViewControllerDelegate {
let backButton = UIButton(type: .system)
var config: JWPlayerConfiguration!
var dismissTapped: (() -> Void)?
var videoIndex : Int?
func rotateToLandsScapeDevice(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
@@ -42,16 +43,14 @@ class PlayerVC: JWPlayerViewController, JWPlayerViewControllerDelegate {
// UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
UIView.setAnimationsEnabled(true)
}
override func viewDidLoad() {
super.viewDidLoad()
self.rotateToLandsScapeDevice()
self.delegate = self
player.configurePlayer(with: config)
// NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.willEnterForegroundNotification, object: nil)
}
@objc func applicationDidBecomeActive() {
self.setDeviceOrientation(orientation: .landscapeRight)
}
@@ -85,11 +84,13 @@ class PlayerVC: JWPlayerViewController, JWPlayerViewControllerDelegate {
override func jwplayer(_ player: any JWPlayer, didFinishLoadingWithTime loadTime: TimeInterval) {
super.jwplayer(player, didFinishLoadingWithTime: loadTime)
print("LoadTime", loadTime)
self.player.play()
// self.player.play()
}
override func jwplayerIsReady(_ player: JWPlayer) {
super.jwplayerIsReady(player)
player.loadPlayerItemAt(index: 4)
player.seek(to: 30)
print("IsReady")
}
@@ -121,7 +122,7 @@ class PlayerVC: JWPlayerViewController, JWPlayerViewControllerDelegate {
override func jwplayer(_ player: JWPlayer, isBufferingWithReason reason: JWBufferReason) {
super.jwplayer(player, isBufferingWithReason: reason)
player.play()
// player.play()
print("Buffering Reason:", reason)
}

View File

@@ -192,60 +192,119 @@ class ThemeOneVM{
vc.liveTVView.isUserInteractionEnabled = true
}
// function which is triggered when handleTap is called
// function which is triggered when handleTap on livetv is called
@objc func handleTap(_ sender: UITapGestureRecognizer) {
Utilities.startProgressHUD(msg: "Loading...")
print("tapped")
let vc = self.vc.storyboard?.instantiateViewController(identifier: "PlayerVC") as! PlayerVC
guard let storyboard = self.vc.storyboard else {
print("Storyboard not found")
Utilities.dismissProgressHUD()
return
}
guard let vc = storyboard.instantiateViewController(identifier: "PlayerVC") as? PlayerVC else {
print("PlayerVC not found")
Utilities.dismissProgressHUD()
return
}
DispatchQueue.main.async {
do {
// Create a JWMediaTrack with the thumbnails .vtt file
// let thumbnailTrack = try JWThumbnailTrackBuilder()
// .file(URL(string:"https://content.jwplatform.com/videos/Agy4RIje-Ysj2G4DQ.mp4")!)
// .build()
// Ensure the liveStreamURL is valid
guard let liveStreamURL = URL(string: self.liveStreamURL) else {
print("Invalid live stream URL")
Utilities.dismissProgressHUD()
return
}
// Create a JWPlayerItem
let item = try JWPlayerItemBuilder()
.file(URL(string: self.liveStreamURL)!)
.file(liveStreamURL)
.title("Testing Title")
// .posterImage(URL(string: "https://img.freepik.com/free-photo/painting-mountain-lake-with-mountain-background_188544-9126.jpg")!)
// .mediaTracks([thumbnailTrack])
.build()
// Create a config, and give it the item as a playlist.
// Create a JWPlayerConfiguration
let config = try JWPlayerConfigurationBuilder()
.playlist(items: [item])
.autostart(true)
// .preload(.auto)
// .repeatContent(true)
.build()
vc.config = config
vc.dismissTapped = self.tapped
vc.modalPresentationStyle = .overFullScreen
Utilities.dismissProgressHUD()
// Present the PlayerVC
self.vc.present(vc, animated: false) {
self.stopLiveStream()
vc.transitionToFullScreen(animated: true) {
print("FullScreen")
}
// vc.setDeviceOrientation(orientation: .landscapeRight)
}
// Utilities.dismissProgressHUD()
// self.vc.navigationController?.pushViewController(vc, animated: true)
// self.stopLiveStream()
}
catch {
// Handle Error
} catch {
print("Error creating JWPlayer configuration: \(error)")
Utilities.dismissProgressHUD()
}
// Dismiss the progress HUD after the view controller presentation
Utilities.dismissProgressHUD()
}
}
// @objc func handleTap(_ sender: UITapGestureRecognizer) {
// Utilities.startProgressHUD(msg: "Loading...")
// print("tapped")
// let vc = self.vc.storyboard?.instantiateViewController(identifier: "PlayerVC") as! PlayerVC
//
// DispatchQueue.main.async {
// do {
// // Create a JWMediaTrack with the thumbnails .vtt file
// // let thumbnailTrack = try JWThumbnailTrackBuilder()
// // .file(URL(string:"https://content.jwplatform.com/videos/Agy4RIje-Ysj2G4DQ.mp4")!)
// // .build()
//
// // Create a JWPlayerItem
// let item = try JWPlayerItemBuilder()
// .file(URL(string: self.liveStreamURL)!)
// .title("Testing Title")
//// .posterImage(URL(string: "https://img.freepik.com/free-photo/painting-mountain-lake-with-mountain-background_188544-9126.jpg")!)
// // .mediaTracks([thumbnailTrack])
// .build()
//
// // Create a config, and give it the item as a playlist.
// let config = try JWPlayerConfigurationBuilder()
// .playlist(items: [item])
// .autostart(true)
//// .preload(.auto)
//// .repeatContent(true)
// .build()
//
// vc.config = config
// vc.dismissTapped = self.tapped
//
// vc.modalPresentationStyle = .overFullScreen
// Utilities.dismissProgressHUD()
// self.vc.present(vc, animated: false) {
// self.stopLiveStream()
// vc.transitionToFullScreen(animated: true) {
// print("FullScreen")
// }
// // vc.setDeviceOrientation(orientation: .landscapeRight)
// }
//// Utilities.dismissProgressHUD()
//// self.vc.navigationController?.pushViewController(vc, animated: true)
//// self.stopLiveStream()
// }
// catch {
// // Handle Error
// }
// }
// }
func tapped(){
Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
self.startLiveStream()
self.avPlayer.play()
self.moveLiveTVView()
self.vc.liveTvPlayer.layoutIfNeeded()
}
print("Sadasd")

View File

@@ -21,6 +21,9 @@ class ThemeTwoVM{
var avPlayer : AVPlayer!
var playerLayer: AVPlayerLayer!
/*
Static cell data
*/
var cellData = [Theme2Struct(imageName: "WokaFMT2", text: "WOKA FM"),
Theme2Struct(imageName: "LiveTVT2", text: "LIVE TV"),
Theme2Struct(imageName: "WebSeriesT2", text: "WEB SERIES"),
@@ -31,11 +34,38 @@ class ThemeTwoVM{
func initView(){
setupCell()
NotificationCenter.default.addObserver(self, selector: #selector(self.reloadTheme), name: NSNotification.Name(rawValue: K.NotificationCenterReloads.reloadTheme), object: nil)
setupAvPlayer()
setUserData()
handleNotificationCenter()
}
private func handleNotificationCenter(){
NotificationCenter.default.addObserver(self, selector: #selector(self.reloadTheme), name: NSNotification.Name(rawValue: K.NotificationCenterReloads.reloadTheme), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
// MARK: - Notification Center Handlers
@objc func reloadTheme(){
self.vc.delegate?.didPressSwitchButton(from: self.vc)
}
@objc func appDidEnterBackground() {
// Code to execute when the app enters the background
print("App entered background")
self.avPlayer.pause()
}
@objc func appWillEnterForeground() {
// Code to execute when the app enters the foreground
print("App will enter foreground")
self.avPlayer.play()
}
// MARK: - Live TV
func playLiveTV(){
Utilities.startProgressHUD(msg: "Loading...")
print("tapped")
@@ -177,11 +207,7 @@ class ThemeTwoVM{
}
// MARK: - Notification Center Handlers
@objc func reloadTheme(){
self.vc.delegate?.didPressSwitchButton(from: self.vc)
}
// MARK: - SetupCell
func setupCell(){
vc.collectionView.register(UINib(nibName: K.CellIdentifier.Theme.homeExploreCell, bundle: nil), forCellWithReuseIdentifier: K.CellIdentifier.Theme.homeExploreCell)

View File

@@ -19,9 +19,16 @@ class WebSeriesSeasonVC: UIViewController {
@IBOutlet weak var seasonDesc: UILabel!
@IBOutlet weak var categoryCV: UICollectionView!
//Episode Outlet
@IBOutlet weak var episodeTitle: UILabel!
@IBOutlet weak var episodeTableView: UITableView!
@IBOutlet weak var tableHeight: NSLayoutConstraint!
//Teaser Outlet
@IBOutlet weak var teaserTableView: UITableView!
@IBOutlet weak var teaserTableHeight: NSLayoutConstraint!
@IBOutlet weak var teaserTitle: LocalisedElementsLabel!
@IBOutlet weak var addIcon: UIImageView!
@IBOutlet weak var addLabel: UILabel!
@@ -63,13 +70,23 @@ class WebSeriesSeasonVC: UIViewController {
@IBAction func watchBtnTapped(_ sender: LocalisedElementsButton) {
}
@IBAction func retryBtnTapped(_ sender: LocalisedElementsButton) {
}
}
// MARK: - TableView DataSource , Delegates
extension WebSeriesSeasonVC : TableViewSRC{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return vm.seasonEpisodeData.count
switch tableView{
case episodeTableView:
return vm.seasonEpisodeData.first?.episodeData?.count ?? 0
case teaserTableView:
return vm.teaserData.count
default:
return 0
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
@@ -78,9 +95,34 @@ extension WebSeriesSeasonVC : TableViewSRC{
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.CellIdentifier.WebSeries.webSeriesEpisodeCell) as! WebSeriesEpisodeCell
switch tableView{
case episodeTableView:
if let data = vm.seasonEpisodeData.first?.episodeData?[indexPath.row]{
cell.setData(data: data)
}
case teaserTableView:
let data = vm.teaserData[indexPath.row]
cell.setTeaserData(data: data)
default:
break
}
cell.btnTapped = { [self] () -> Void in
var urls = [String]()
var title = [String]()
if let episodeData = vm.seasonEpisodeData.first?.episodeData{
for i in episodeData{
urls.append(i.episodeURL ?? "")
title.append(i.episodeTitle ?? "Episode")
}
}
if let data = vm.seasonEpisodeData.first?.episodeData?[indexPath.row] , let url = data.episodeURL{
JWPlayerManager.shared.presentPlayer(from: self, withURLs: urls, titles: title,startIndex: indexPath.row)
}
}
let data = vm.seasonEpisodeData[indexPath.row]
cell.setData(data: data)
return cell
}
}
@@ -103,7 +145,11 @@ extension WebSeriesSeasonVC : CollectionViewSRC{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
vm.episodeSelectedCateogory = vm.seasonListingData[indexPath.row].id
vm.setSeasonData()
episodeTitle.isHidden = false
episodeTitle.text = ""
vm.getSeasonEpisode()
vm.getTeaserListing()
self.categoryCV.reloadData()
}
}

View File

@@ -60,6 +60,9 @@ class WebSeriesVC: UIViewController {
// MARK: - Tap Handler
@IBAction func playTrailer(_ sender: LocalisedElementsButton) {
}
@IBAction func expandLanguageTapped(_ sender: UIButton) {
vm.dropDownModule.show()
self.expandBtn.setImage(UIImage(named: "SupportUpArrow"), for: .normal)

View File

@@ -0,0 +1,141 @@
//
// JWPlayerManager.swift
// WOKA
//
// Created by MacBook Pro on 24/06/24.
//
import UIKit
import JWPlayerKit
class JWPlayerManager {
static let shared = JWPlayerManager()
private init() {}
func presentPlayer(from viewController: UIViewController, withURLs liveStreamURLs: [String], titles: [String]? = nil, startIndex: Int = 0, completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
Utilities.startProgressHUD(msg: "Loading...")
}
let sb = UIStoryboard(name: K.StoryBoard.theme, bundle: nil)
guard let playerVC = sb.instantiateViewController(identifier: "PlayerVC") as? PlayerVC else {
print("PlayerVC not found")
Utilities.dismissProgressHUD()
return
}
DispatchQueue.main.async {
do {
// Create an array to hold the JWPlayerItems
var items: [JWPlayerItem] = []
// Ensure the liveStreamURLs and titles arrays have the same count, if titles are provided
if let titles = titles, titles.count != liveStreamURLs.count {
print("Titles count does not match URLs count")
Utilities.dismissProgressHUD()
return
}
// Iterate over the liveStreamURLs to create JWPlayerItems
for (index, urlString) in liveStreamURLs.enumerated() {
guard let url = URL(string: urlString) else {
print("Invalid live stream URL at index \(index)")
continue
}
// Use the corresponding title if provided, otherwise use a default title
let itemTitle = titles?[index] ?? "Default Title \(index + 1)"
let item = try JWPlayerItemBuilder()
.file(url)
.title(itemTitle)
.build()
items.append(item)
}
// Ensure there is at least one valid item
guard !items.isEmpty else {
print("No valid items to play")
Utilities.dismissProgressHUD()
return
}
// Create a JWPlayerConfiguration with the playlist
let config = try JWPlayerConfigurationBuilder()
.playlist(items: items)
.autostart(false)
.build()
playerVC.videoIndex = startIndex
playerVC.config = config
playerVC.modalPresentationStyle = .overFullScreen
// Present the PlayerVC
viewController.present(playerVC, animated: false) {
completion?()
playerVC.transitionToFullScreen(animated: true) {
print("FullScreen")
}
}
} catch {
print("Error creating JWPlayer configuration: \(error)")
Utilities.dismissProgressHUD()
}
// Dismiss the progress HUD after the view controller presentation
Utilities.dismissProgressHUD()
}
}
// func presentPlayer(from viewController: UIViewController, withURL liveStreamURL: String, title: String = "Testing Title", completion: (() -> Void)? = nil) {
// Utilities.startProgressHUD(msg: "Loading...")
//
// let sb = UIStoryboard(name: K.StoryBoard.theme, bundle: nil)
// guard let playerVC = sb.instantiateViewController(identifier: "PlayerVC") as? PlayerVC else {
// print("PlayerVC not found")
// Utilities.dismissProgressHUD()
// return
// }
//
// DispatchQueue.main.async {
// do {
// // Ensure the liveStreamURL is valid
// guard let url = URL(string: liveStreamURL) else {
// print("Invalid live stream URL")
// Utilities.dismissProgressHUD()
// return
// }
//
// // Create a JWPlayerItem
// let item = try JWPlayerItemBuilder()
// .file(url)
// .title(title)
// .build()
//
// // Create a JWPlayerConfiguration
// let config = try JWPlayerConfigurationBuilder()
// .playlist(items: [item])
// .autostart(true)
// .build()
//
// playerVC.config = config
// playerVC.modalPresentationStyle = .overFullScreen
//
// // Present the PlayerVC
// viewController.present(playerVC, animated: false) {
// completion?()
// playerVC.transitionToFullScreen(animated: true) {
// print("FullScreen")
// }
// }
// } catch {
// print("Error creating JWPlayer configuration: \(error)")
// Utilities.dismissProgressHUD()
// }
//
// // Dismiss the progress HUD after the view controller presentation
// Utilities.dismissProgressHUD()
// }
// }
}

View File

@@ -0,0 +1,85 @@
//
// TeaserDM.swift
// WOKA
//
// Created by MacBook Pro on 24/06/24.
//
import Foundation
// MARK: - TeaserDM
struct TeaserDM: Codable {
let result: [ResultData]?
let totalRecords: Int?
enum CodingKeys: String, CodingKey {
case result
case totalRecords = "total_records"
}
// MARK: - ResultData
struct ResultData: Codable {
let id, watchShowsMasterID, seasonMasterID, teaserNumber: Int?
let serialNumber: Int?
let teaserTitle, teaserDescription: String?
let thumbnailPath: String?
let thumbnailImgURL: String?
let teaserURL: String?
let languageMasterID: Int?
let tagsKeyword, teaserDuration, releaseDate: String?
let contentMoreDetails: [ContentMoreDetail]?
let seasonData: SeasonData?
// let userVideoView: [JSONAny]?
enum CodingKeys: String, CodingKey {
case id
case watchShowsMasterID = "watch_shows_master_id"
case seasonMasterID = "season_master_id"
case teaserNumber = "teaser_number"
case serialNumber = "serial_number"
case teaserTitle = "teaser_title"
case teaserDescription = "teaser_description"
case thumbnailPath = "thumbnail_path"
case thumbnailImgURL = "thumbnail_img_url"
case teaserURL = "teaser_url"
case languageMasterID = "language_master_id"
case tagsKeyword = "tags_keyword"
case teaserDuration = "teaser_duration"
case releaseDate = "release_date"
case contentMoreDetails = "content_more_details"
case seasonData = "season_data"
// case userVideoView = "user_video_view"
}
}
// MARK: - ContentMoreDetail
struct ContentMoreDetail: Codable {
let id, contentID, postType, languageMasterID: Int?
let title, description, tagsKeywords: String?
let contentHDURL, contentSDURL: String?
enum CodingKeys: String, CodingKey {
case id
case contentID = "content_id"
case postType = "post_type"
case languageMasterID = "language_master_id"
case title, description
case tagsKeywords = "tags_keywords"
case contentHDURL = "content_hd_url"
case contentSDURL = "content_sd_url"
}
}
// MARK: - SeasonData
struct SeasonData: Codable {
let id, watchShowsMasterID: Int?
let seasonNumber: String?
enum CodingKeys: String, CodingKey {
case id
case watchShowsMasterID = "watch_shows_master_id"
case seasonNumber = "season_number"
}
}
}

View File

@@ -14,8 +14,12 @@ class WebSeriesEpisodeCell: UITableViewCell {
@IBOutlet weak var seasonTime: UILabel!
@IBOutlet weak var playBtn: UIButton!
typealias btnTappedBlock = () -> Void // 0 - plus 1 - minus
var btnTapped : btnTappedBlock!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
@@ -25,18 +29,37 @@ class WebSeriesEpisodeCell: UITableViewCell {
// Configure the view for the selected state
}
func setData(data : SeasonEpisodeListingDM.ResultData){
func setData(data : SeasonEpisodeListingDM.EpisodeDatum){
if let url = data.thumbnailPath{
self.seasonImage.imageURL(url, color: UIColor.appColor(.TextDarkBlue)!)
}
self.seasonTime.text = data.episodeDuration ?? "0:00:00"
if AuthFunc.shareInstance.getDefaultLanguage() == .english{
seasonTitle.text = data.seasonMoreDetails?.filter({$0.languageMasterID == 1}).first?.title
seasonTitle.text = data.contentMoreDetails?.filter({$0.languageMasterID == 1}).first?.title
}else{
seasonTitle.text = data.seasonMoreDetails?.filter({$0.languageMasterID == 2}).first?.title
seasonTitle.text = data.contentMoreDetails?.filter({$0.languageMasterID == 2}).first?.title
}
}
func setTeaserData(data : TeaserDM.ResultData){
if let url = data.thumbnailPath{
self.seasonImage.imageURL(url, color: UIColor.appColor(.TextDarkBlue)!)
}
self.seasonTime.text = data.teaserDuration ?? "0:00:00"
if AuthFunc.shareInstance.getDefaultLanguage() == .english{
seasonTitle.text = data.contentMoreDetails?.filter({$0.languageMasterID == 1}).first?.title
}else{
seasonTitle.text = data.contentMoreDetails?.filter({$0.languageMasterID == 2}).first?.title
}
}
@IBAction func playBtnTapped(_ sender: UIButton) {
if btnTapped != nil {
btnTapped()
}
}
}

View File

@@ -6,7 +6,6 @@
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<customFonts key="customFonts">
@@ -42,10 +41,10 @@
</userDefinedRuntimeAttributes>
</imageView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="RoA-pj-myv">
<rect key="frame" x="138" y="11" width="211" height="76"/>
<rect key="frame" x="138" y="11" width="201" height="76"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oux-y5-3S0">
<rect key="frame" x="0.0" y="0.0" width="211" height="54"/>
<rect key="frame" x="0.0" y="0.0" width="201" height="54"/>
<string key="text">Episodes
Episodes
Episodes</string>
@@ -54,38 +53,28 @@ Episodes</string>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Episodes" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wAx-mz-nFB">
<rect key="frame" x="0.0" y="59" width="211" height="17"/>
<rect key="frame" x="0.0" y="59" width="201" height="17"/>
<fontDescription key="fontDescription" name="Exo2-Regular" family="Exo 2" pointSize="14"/>
<color key="textColor" name="TextDarkBlue"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="b4V-gS-TvG">
<rect key="frame" x="359" y="8" width="40" height="82"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uFk-k1-Xsm">
<rect key="frame" x="0.0" y="21" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" secondItem="uFk-k1-Xsm" secondAttribute="height" multiplier="1:1" id="o0n-9n-a4G"/>
</constraints>
<color key="tintColor" name="TextDarkBlue"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="PlayButton"/>
<connections>
<action selector="playBtnTapped:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="Twk-L6-Wxv"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uFk-k1-Xsm">
<rect key="frame" x="349" y="24" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="LDG-0X-ybq"/>
<constraint firstAttribute="trailing" secondItem="uFk-k1-Xsm" secondAttribute="trailing" id="QqG-Bb-GB2"/>
<constraint firstItem="uFk-k1-Xsm" firstAttribute="centerY" secondItem="b4V-gS-TvG" secondAttribute="centerY" id="WvO-ca-78l"/>
<constraint firstItem="uFk-k1-Xsm" firstAttribute="centerX" secondItem="b4V-gS-TvG" secondAttribute="centerX" id="p5o-kE-Cch"/>
<constraint firstItem="uFk-k1-Xsm" firstAttribute="leading" secondItem="b4V-gS-TvG" secondAttribute="leading" id="r6e-Fk-yoc"/>
<constraint firstAttribute="width" constant="50" id="Moi-sa-43Q"/>
<constraint firstAttribute="width" secondItem="uFk-k1-Xsm" secondAttribute="height" multiplier="1:1" id="o0n-9n-a4G"/>
</constraints>
</view>
<color key="tintColor" name="TextDarkBlue"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="PlayButton">
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/>
</state>
<connections>
<action selector="playBtnTapped:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="Twk-L6-Wxv"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="layoutMargins" top="8" left="8" bottom="8" right="8"/>
@@ -115,12 +104,9 @@ Episodes</string>
</tableViewCell>
</objects>
<resources>
<image name="PlayButton" width="32" height="32"/>
<image name="PlayButton" width="42.666667938232422" height="42.666667938232422"/>
<namedColor name="TextDarkBlue">
<color red="0.10599999874830246" green="0.050999999046325684" blue="0.60399997234344482" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@@ -12,7 +12,6 @@ class WebSeriesSeasonVM{
weak var vc : WebSeriesSeasonVC!
// var watchShowID : Int?
var categoryID : Int?
var episodeSelectedCateogory : Int?
@@ -21,6 +20,8 @@ class WebSeriesSeasonVM{
var seasonEpisodeData = [SeasonEpisodeListingDM.ResultData]()
var teaserData = [TeaserDM.ResultData]()
var showData : WebSeriesShowListDM.ShowDatum?
func initView(){
@@ -67,13 +68,6 @@ class WebSeriesSeasonVM{
vc.addIcon.image = UIImage(systemName: "heart")
vc.addLabel.text = "Add"
}
// switch favourite{
// case true:
// favBtn.setImage(UIImage(systemName: "heart.fill"), for: .normal)
// case false:
// favBtn.setImage(UIImage(systemName: "heart"), for: .normal)
//
// }
}
}
@@ -85,6 +79,10 @@ class WebSeriesSeasonVM{
vc.episodeTableView.register(UINib(nibName: K.CellIdentifier.WebSeries.webSeriesEpisodeCell, bundle: nil), forCellReuseIdentifier: K.CellIdentifier.WebSeries.webSeriesEpisodeCell)
vc.episodeTableView.delegate = vc.self
vc.episodeTableView.dataSource = vc.self
vc.teaserTableView.register(UINib(nibName: K.CellIdentifier.WebSeries.webSeriesEpisodeCell, bundle: nil), forCellReuseIdentifier: K.CellIdentifier.WebSeries.webSeriesEpisodeCell)
vc.teaserTableView.delegate = vc.self
vc.teaserTableView.dataSource = vc.self
}
// MARK: - Api Calls
@@ -113,9 +111,10 @@ class WebSeriesSeasonVM{
Utilities.dismissProgressHUD()
guard let data = data.data?.result else{return}
self.seasonListingData = data
setSeasonData()
episodeSelectedCateogory = seasonListingData.first?.id
setSeasonData()
getSeasonEpisode()
getTeaserListing()
self.vc.categoryCV.reloadData()
default:
break
@@ -132,12 +131,12 @@ class WebSeriesSeasonVM{
}
func setSeasonData(){
guard let data = seasonListingData.first else{return}
guard let data = seasonListingData.filter({$0.id == episodeSelectedCateogory}).first else{return}
vc.seasonImage.imageURL(data.thumbnailPath!, color: UIColor.appColor(.TextDarkBlue)!)
if AuthFunc.shareInstance.getDefaultLanguage() == .english{
let englishData = data.seasonMoreDetails?.filter({$0.languageMasterID == 1}).first
vc.seasonTitle.text = englishData?.title
vc.seasonTitle.text = (englishData?.title ?? "NA") + "\n" + (data.seasonNumber ?? "NA")
if let desc = englishData?.description?.replacingOccurrences(of: "<br>", with: "").htmlToAttributedString{
let sizeText = NSMutableAttributedString(attributedString: desc)
@@ -146,7 +145,7 @@ class WebSeriesSeasonVM{
}
}else{
let hindiData = data.seasonMoreDetails?.filter({$0.languageMasterID == 2}).first
vc.seasonTitle.text = hindiData?.title
vc.seasonTitle.text = (hindiData?.title ?? "NA") + "\n" + (data.seasonNumber ?? "NA")
if let desc = hindiData?.description?.replacingOccurrences(of: "<br>", with: "").htmlToAttributedString{
let sizeText = NSMutableAttributedString(attributedString: desc)
sizeText.setFontFace(font: FontCustom.shareInstance.customFont(fontName: .Exo2_Regular, size: 15),color: UIColor.appColor(.TextDarkBlue)!)
@@ -184,6 +183,16 @@ class WebSeriesSeasonVM{
guard let data = data.data?.result else{return}
self.seasonEpisodeData.removeAll()
self.seasonEpisodeData = data
self.seasonEpisodeData = self.seasonEpisodeData.filter {
$0.episodeData != nil && !$0.episodeData!.isEmpty
}
if self.seasonEpisodeData.count == 0{
self.vc.episodeTitle.isHidden = true
self.vc.episodeTitle.text = ""
}else{
self.vc.episodeTitle.isHidden = false
self.vc.episodeTitle.text = data.first?.mediaType?.uppercased()
}
self.vc.episodeTableView.reloadData()
self.vc.tableHeight.constant = self.vc.episodeTableView.contentSize.height + 100
@@ -202,4 +211,54 @@ class WebSeriesSeasonVM{
}
}
}
func getTeaserListing(){
guard let watchShowID = showData?.id, let episodeSelectedCateogory else{return}
let headers : HTTPHeaders = ["access-token" : AuthFunc.shareInstance.getAccessToken()]
let params : Parameters = ["watch_show_master_id" : watchShowID,
"season_master_id" : episodeSelectedCateogory]
NetworkManager.shareInstance.apiRequest(url: APIEndPoints.WebSeries.teaser_listing, method: .post,parameters: params,headers : headers) { [weak self](result : Result<BaseResponseModel<TeaserDM>, NetworkManager.APIError>) in
switch result{
case .success(let data):
guard let self else{
Utilities.dismissProgressHUD()
return
}
switch data.success{
case 0:
/*
Error
*/
Utilities.dismissProgressHUD()
self.teaserData.removeAll()
self.reloadTeaserTable()
self.vc.teaserTitle.isHidden = true
// vc.toast(msg: data.message ?? "Unrecognised error" , time: 2)
case 1:
Utilities.dismissProgressHUD()
guard let data = data.data?.result else{return}
self.teaserData.removeAll()
self.teaserData = data
self.reloadTeaserTable()
self.vc.teaserTitle.isHidden = false
default:
break
}
case .failure(let error):
guard let self else{
Utilities.dismissProgressHUD()
return
}
Utilities.dismissProgressHUD()
vc.toast(msg: error.localizedDescription , time: 2)
}
}
}
private func reloadTeaserTable(){
self.vc.teaserTableView.reloadData()
self.vc.teaserTableHeight.constant = self.vc.teaserTableView.contentSize.height + 100
self.vc.teaserTableView.layoutIfNeeded()
self.vc.teaserTableHeight.constant = self.vc.teaserTableView.contentSize.height
}
}

View File

@@ -35,14 +35,42 @@
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="MasilaComingSoon" translatesAutoresizingMaskIntoConstraints="NO" id="wnB-dE-0v3">
<rect key="frame" x="0.0" y="0.0" width="393" height="200"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="orj-ne-be7" customClass="LocalisedElementsButton" customModule="WOKA" customModuleProvider="target">
<rect key="frame" x="136.66666666666666" y="77.666666666666657" width="119.99999999999997" height="45"/>
<color key="backgroundColor" red="0.035294117649999998" green="0.0" blue="0.36470588240000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="sN9-Fh-ZMR"/>
<constraint firstAttribute="height" constant="45" id="zYM-Uz-Qxe">
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="22.5"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</constraint>
</constraints>
<fontDescription key="fontDescription" name="Exo2-Bold" family="Exo 2" pointSize="14"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="PLAY TRAILER"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<integer key="value" value="20"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="playTrailer:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="nTB-Is-3cV"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="orj-ne-be7" firstAttribute="centerY" secondItem="bTO-Ql-tyN" secondAttribute="centerY" id="A2N-UU-G5r"/>
<constraint firstAttribute="bottom" secondItem="wnB-dE-0v3" secondAttribute="bottom" id="BG4-7Q-bIb"/>
<constraint firstAttribute="trailing" secondItem="wnB-dE-0v3" secondAttribute="trailing" id="JQb-FA-v0E"/>
<constraint firstItem="wnB-dE-0v3" firstAttribute="top" secondItem="bTO-Ql-tyN" secondAttribute="top" id="LAu-Iu-1sV"/>
<constraint firstAttribute="height" constant="200" id="X2g-lr-CX0"/>
<constraint firstItem="wnB-dE-0v3" firstAttribute="leading" secondItem="bTO-Ql-tyN" secondAttribute="leading" id="sQQ-a5-rhV"/>
<constraint firstItem="orj-ne-be7" firstAttribute="centerX" secondItem="bTO-Ql-tyN" secondAttribute="centerX" id="x74-Vl-ZwA"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
@@ -103,7 +131,7 @@
<stackView hidden="YES" opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="mFz-UO-YUA">
<rect key="frame" x="10" y="-270" width="373" height="270"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CONTINUE WATCHING" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="l9e-Rq-ZI5">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CONTINUE WATCHING" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="l9e-Rq-ZI5" customClass="LocalisedElementsLabel" customModule="WOKA" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="373" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="MAT-Vz-Ee8"/>
@@ -265,7 +293,7 @@
<rect key="frame" x="0.0" y="59" width="393" height="793"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="JVZ-ee-mig">
<rect key="frame" x="0.0" y="0.0" width="393" height="623"/>
<rect key="frame" x="0.0" y="0.0" width="393" height="681.33333333333337"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8zm-IG-hTk">
<rect key="frame" x="0.0" y="0.0" width="393" height="553"/>
@@ -376,16 +404,19 @@
<rect key="frame" x="0.0" y="453" width="373" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PzY-0K-yPM" customClass="LocalisedElementsButton" customModule="WOKA" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="140" height="50"/>
<rect key="frame" x="0.0" y="0.0" width="150" height="50"/>
<color key="backgroundColor" red="0.36862745099999999" green="0.1215686275" blue="0.76862745099999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="140" id="UqM-B3-bVp"/>
<constraint firstAttribute="width" constant="150" id="UqM-B3-bVp"/>
<constraint firstAttribute="height" constant="50" id="ldT-CD-cmn"/>
</constraints>
<fontDescription key="fontDescription" name="Exo2-Bold" family="Exo 2" pointSize="18"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<inset key="titleEdgeInsets" minX="10" minY="0.0" maxX="0.0" maxY="0.0"/>
<state key="normal" title="WATCH" image="PlayButton"/>
<inset key="titleEdgeInsets" minX="15" minY="0.0" maxX="0.0" maxY="0.0"/>
<inset key="imageEdgeInsets" minX="0.0" minY="5" maxX="10" maxY="5"/>
<state key="normal" title="WATCH" image="PlayButton">
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="small"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<integer key="value" value="25"/>
@@ -515,13 +546,54 @@
</subviews>
<edgeInsets key="layoutMargins" top="0.0" left="5" bottom="0.0" right="5"/>
</stackView>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="244" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="RJP-uy-ll3">
<rect key="frame" x="0.0" y="623" width="393" height="0.0"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" id="F3X-r1-O8N"/>
</constraints>
</tableView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="Idg-Al-Eib" userLabel="EpisodeStack">
<rect key="frame" x="0.0" y="623" width="393" height="26.666666666666629"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="cd8-G8-MXm">
<rect key="frame" x="0.0" y="0.0" width="393" height="21.666666666666668"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SONGS" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yzq-z1-mIE">
<rect key="frame" x="10" y="0.0" width="373" height="21.666666666666668"/>
<fontDescription key="fontDescription" name="Exo2-Bold" family="Exo 2" pointSize="18"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<edgeInsets key="layoutMargins" top="0.0" left="10" bottom="0.0" right="10"/>
</stackView>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="244" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="RJP-uy-ll3">
<rect key="frame" x="0.0" y="26.666666666666629" width="393" height="0.0"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" id="F3X-r1-O8N"/>
</constraints>
</tableView>
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="4WM-II-43s" userLabel="Teaser Stack">
<rect key="frame" x="0.0" y="654.66666666666663" width="393" height="26.666666666666629"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="vrf-Fc-axK">
<rect key="frame" x="0.0" y="0.0" width="393" height="21.666666666666668"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="TEASERS" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dfh-1T-Hnq" customClass="LocalisedElementsLabel" customModule="WOKA" customModuleProvider="target">
<rect key="frame" x="10" y="0.0" width="373" height="21.666666666666668"/>
<fontDescription key="fontDescription" name="Exo2-Bold" family="Exo 2" pointSize="18"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<edgeInsets key="layoutMargins" top="0.0" left="10" bottom="0.0" right="10"/>
</stackView>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="244" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="hvm-6m-itF">
<rect key="frame" x="0.0" y="26.666666666666742" width="393" height="0.0"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" id="KQj-Vb-7pU"/>
</constraints>
</tableView>
</subviews>
</stackView>
</subviews>
</stackView>
</subviews>
@@ -553,6 +625,7 @@
<outlet property="addStack" destination="iyr-1k-HHN" id="PBF-2C-nPZ"/>
<outlet property="categoryCV" destination="rAO-uI-Djj" id="9vd-ge-YZ5"/>
<outlet property="episodeTableView" destination="RJP-uy-ll3" id="EEa-5i-fyE"/>
<outlet property="episodeTitle" destination="yzq-z1-mIE" id="cfL-Qn-sa3"/>
<outlet property="likeIcon" destination="f9M-ON-9gL" id="Iyp-MI-UfT"/>
<outlet property="likeLabel" destination="Q4V-Gl-g6v" id="p5K-OM-Lmn"/>
<outlet property="likeStack" destination="8UJ-rG-fB2" id="Ktz-EP-lgH"/>
@@ -564,6 +637,9 @@
<outlet property="seasonTitle" destination="AdX-OP-Gcd" id="I39-xA-Z1d"/>
<outlet property="shareStack" destination="Lag-16-uzP" id="jjy-Lv-6on"/>
<outlet property="tableHeight" destination="F3X-r1-O8N" id="O70-hN-czC"/>
<outlet property="teaserTableHeight" destination="KQj-Vb-7pU" id="VEU-Ws-iAi"/>
<outlet property="teaserTableView" destination="hvm-6m-itF" id="4Sl-Om-Pu6"/>
<outlet property="teaserTitle" destination="dfh-1T-Hnq" id="JeN-E5-D7C"/>
<outlet property="totalLikes" destination="Egv-Cu-aPK" id="uTn-bf-zWw"/>
</connections>
</viewController>
@@ -574,7 +650,7 @@
</scenes>
<resources>
<image name="MasilaComingSoon" width="200" height="100"/>
<image name="PlayButton" width="32" height="32"/>
<image name="PlayButton" width="42.666667938232422" height="42.666667938232422"/>
<image name="ShareImage" width="18" height="18"/>
<image name="SupportBottomArrow" width="16.333333969116211" height="16.333333969116211"/>
<image name="WebSeriesSeasonsBackground" width="142.66667175292969" height="187.33332824707031"/>