diff --git a/Podfile b/Podfile index 8dfabab..c3c69f0 100644 --- a/Podfile +++ b/Podfile @@ -35,4 +35,17 @@ target 'ThimraTV' do pod 'Adjust' # Adjust + pod 'GoogleMobileAdsMediationAppLovin' + pod 'GoogleMobileAdsMediationIronSource' + pod 'GoogleMobileAdsMediationFacebook' + pod 'GoogleMobileAdsMediationMintegral' + pod 'GoogleMobileAdsMediationPangle' + + pod 'AppLovinMediationFacebookAdapter' + pod 'AppLovinMediationByteDanceAdapter' + pod 'AppLovinMediationIronSourceAdapter' + pod 'AppLovinMediationMintegralAdapter' + pod 'AppLovinMediationGoogleAdapter' + + end diff --git a/Podfile.lock b/Podfile.lock index 44ecdf3..bb60d68 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -4,13 +4,110 @@ PODS: - Adjust/Adjust (5.4.0): - AdjustSignature (= 3.35.2) - AdjustSignature (3.35.2) + - Ads-Global (7.1.1.1): + - Ads-Global/BUAdSDK (= 7.1.1.1) + - Ads-Global/BUAdSDK (7.1.1.1) - Alamofire (5.10.2) + - AppLovinMediationByteDanceAdapter (7.1.1.1.0): + - Ads-Global (= 7.1.1.1) + - AppLovinSDK (>= 13.0.0) + - AppLovinMediationFacebookAdapter (6.17.1.0): + - AppLovinSDK (>= 13.0.0) + - FBAudienceNetwork (= 6.17.1) + - AppLovinMediationGoogleAdapter (12.5.0.0): + - AppLovinSDK (>= 13.0.0) + - Google-Mobile-Ads-SDK (= 12.5.0) + - AppLovinMediationIronSourceAdapter (8.9.0.0.0): + - AppLovinSDK (>= 13.0.0) + - IronSourceSDK (= 8.9.0.0) + - AppLovinMediationMintegralAdapter (7.7.8.0.0): + - AppLovinSDK (>= 13.0.0) + - MintegralAdSDK (= 7.7.8) + - MintegralAdSDK/BidSplashAd (= 7.7.8) + - AppLovinSDK (13.3.0) - CocoaAsyncSocket (7.6.5) - EmptyDataSet-Swift (5.0.0) + - FBAudienceNetwork (6.17.1) + - Google-Mobile-Ads-SDK (12.5.0): + - GoogleUserMessagingPlatform (>= 1.1) + - GoogleMobileAdsMediationAppLovin (13.3.0.0): + - AppLovinSDK (= 13.3.0) + - Google-Mobile-Ads-SDK (~> 12.0) + - GoogleMobileAdsMediationFacebook (6.17.1.0): + - FBAudienceNetwork (= 6.17.1) + - Google-Mobile-Ads-SDK (~> 12.0) + - GoogleMobileAdsMediationIronSource (8.9.0.0.0): + - Google-Mobile-Ads-SDK (~> 12.0) + - IronSourceSDK (= 8.9.0.0) + - GoogleMobileAdsMediationMintegral (7.7.8.0): + - Google-Mobile-Ads-SDK (~> 12.0) + - MintegralAdSDK/All (= 7.7.8) + - GoogleMobileAdsMediationPangle (7.1.1.1.0): + - Ads-Global (= 7.1.1.1) + - Google-Mobile-Ads-SDK (~> 12.0) + - GoogleUserMessagingPlatform (3.0.0) - HWPanModal (0.9.9) + - IronSourceAdQualitySDK (7.24.3) + - IronSourceSDK (8.9.0.0): + - IronSourceSDK/AdQuality (= 8.9.0.0) + - IronSourceSDK/Ads (= 8.9.0.0) + - IronSourceSDK/AdQuality (8.9.0.0): + - IronSourceAdQualitySDK (~> 7.24.3) + - IronSourceSDK/Ads (8.9.0.0) - Kingfisher (8.3.2) - KTVHTTPCache (3.0.2): - CocoaAsyncSocket + - MintegralAdSDK (7.7.8): + - MintegralAdSDK/BannerAd (= 7.7.8) + - MintegralAdSDK/BidBannerAd (= 7.7.8) + - MintegralAdSDK/BidInterstitialVideoAd (= 7.7.8) + - MintegralAdSDK/BidNativeAd (= 7.7.8) + - MintegralAdSDK/BidNewInterstitialAd (= 7.7.8) + - MintegralAdSDK/BidRewardVideoAd (= 7.7.8) + - MintegralAdSDK/InterstitialVideoAd (= 7.7.8) + - MintegralAdSDK/NativeAd (= 7.7.8) + - MintegralAdSDK/NewInterstitialAd (= 7.7.8) + - MintegralAdSDK/RewardVideoAd (= 7.7.8) + - MintegralAdSDK/All (7.7.8): + - MintegralAdSDK/BannerAd + - MintegralAdSDK/BidNativeAd + - MintegralAdSDK/InterstitialVideoAd + - MintegralAdSDK/NativeAd + - MintegralAdSDK/NativeAdvancedAd + - MintegralAdSDK/NewInterstitialAd + - MintegralAdSDK/RewardVideoAd + - MintegralAdSDK/SplashAd + - MintegralAdSDK/BannerAd (7.7.8): + - MintegralAdSDK/NativeAd + - MintegralAdSDK/BidBannerAd (7.7.8): + - MintegralAdSDK/BannerAd + - MintegralAdSDK/BidNativeAd + - MintegralAdSDK/BidInterstitialVideoAd (7.7.8): + - MintegralAdSDK/BidNativeAd + - MintegralAdSDK/InterstitialVideoAd + - MintegralAdSDK/BidNativeAd (7.7.8): + - MintegralAdSDK/NativeAd + - MintegralAdSDK/BidNewInterstitialAd (7.7.8): + - MintegralAdSDK/BidNativeAd + - MintegralAdSDK/NewInterstitialAd + - MintegralAdSDK/BidRewardVideoAd (7.7.8): + - MintegralAdSDK/BidNativeAd + - MintegralAdSDK/RewardVideoAd + - MintegralAdSDK/BidSplashAd (7.7.8): + - MintegralAdSDK/BidNativeAd + - MintegralAdSDK/SplashAd + - MintegralAdSDK/InterstitialVideoAd (7.7.8): + - MintegralAdSDK/NativeAd + - MintegralAdSDK/NativeAd (7.7.8) + - MintegralAdSDK/NativeAdvancedAd (7.7.8): + - MintegralAdSDK/NativeAd + - MintegralAdSDK/NewInterstitialAd (7.7.8): + - MintegralAdSDK/InterstitialVideoAd + - MintegralAdSDK/NativeAd + - MintegralAdSDK/RewardVideoAd (7.7.8): + - MintegralAdSDK/NativeAd + - MintegralAdSDK/SplashAd (7.7.8): + - MintegralAdSDK/NativeAd - MJRefresh (3.7.9) - Moya (15.0.0): - Moya/Core (= 15.0.0) @@ -40,7 +137,17 @@ PODS: DEPENDENCIES: - Adjust + - AppLovinMediationByteDanceAdapter + - AppLovinMediationFacebookAdapter + - AppLovinMediationGoogleAdapter + - AppLovinMediationIronSourceAdapter + - AppLovinMediationMintegralAdapter - EmptyDataSet-Swift + - GoogleMobileAdsMediationAppLovin + - GoogleMobileAdsMediationFacebook + - GoogleMobileAdsMediationIronSource + - GoogleMobileAdsMediationMintegral + - GoogleMobileAdsMediationPangle - HWPanModal - Kingfisher - KTVHTTPCache @@ -60,12 +167,30 @@ SPEC REPOS: https://github.com/CocoaPods/Specs.git: - Adjust - AdjustSignature + - Ads-Global - Alamofire + - AppLovinMediationByteDanceAdapter + - AppLovinMediationFacebookAdapter + - AppLovinMediationGoogleAdapter + - AppLovinMediationIronSourceAdapter + - AppLovinMediationMintegralAdapter + - AppLovinSDK - CocoaAsyncSocket - EmptyDataSet-Swift + - FBAudienceNetwork + - Google-Mobile-Ads-SDK + - GoogleMobileAdsMediationAppLovin + - GoogleMobileAdsMediationFacebook + - GoogleMobileAdsMediationIronSource + - GoogleMobileAdsMediationMintegral + - GoogleMobileAdsMediationPangle + - GoogleUserMessagingPlatform - HWPanModal + - IronSourceAdQualitySDK + - IronSourceSDK - Kingfisher - KTVHTTPCache + - MintegralAdSDK - MJRefresh - Moya - ReachabilitySwift @@ -81,12 +206,30 @@ SPEC REPOS: SPEC CHECKSUMS: Adjust: a5f881d0cbfe9a6df979b076dc7116fe19ece797 AdjustSignature: 23b9e5d4adcadffc303bb6b410fde617dd88504f + Ads-Global: b81917c405e94ad38dd45022f0fe17042429f578 Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496 + AppLovinMediationByteDanceAdapter: 82f5a49494a40a3dfa4b959d6f089986c924511b + AppLovinMediationFacebookAdapter: 413c7188e2a21e4c9256aa2b096ddc27240b1443 + AppLovinMediationGoogleAdapter: 36d9926e84420b49a7cba45800c6eada3be58b05 + AppLovinMediationIronSourceAdapter: 240be5366f735418d01f52304a0dc9c3d521e546 + AppLovinMediationMintegralAdapter: 5f64aa8427ba19efea2e0d583abc22dc4633705b + AppLovinSDK: 5ed9dbada0de5a80e4546116994470011ed01f53 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 EmptyDataSet-Swift: eb382c0c87a2d9c678077385a595cec52da38171 + FBAudienceNetwork: eb3ffbf2b35df25e21e163657044ffef66616a40 + Google-Mobile-Ads-SDK: 6df9aadcee32bce0ff05bcad56ef2c88a4a5e82b + GoogleMobileAdsMediationAppLovin: efd0c6ebe304747b61e115ddc5bd0ed28346f499 + GoogleMobileAdsMediationFacebook: 750a2c14ce0bdf813e4e1d4ab4aa283f72f8a875 + GoogleMobileAdsMediationIronSource: e0e19ab5f8af55aacb30612ae1c84093db1e2e6e + GoogleMobileAdsMediationMintegral: 0aefaa2f0608c210eb26dd6a6207d8c1bd992bbd + GoogleMobileAdsMediationPangle: 62f9bdabf7ed168f6b0f04831c1cc04da0eec10e + GoogleUserMessagingPlatform: f8d0cdad3ca835406755d0a69aa634f00e76d576 HWPanModal: b57a6717d3cdcd666bff44f9dd2a5be9f4d6f5d2 + IronSourceAdQualitySDK: 9974aea1ec73b24fd0eb7e74e7936bcae9fead1d + IronSourceSDK: 285da6cf55cb80a1b258260ee27df8332552ec0d Kingfisher: 0621d0ac0c78fecb19f6dc5303bde2b52abaf2f5 KTVHTTPCache: 5711692cdf9a5ecfe829b1e16577deb3ffe3dc86 + MintegralAdSDK: 07e018290689b906499d8285654bba42681cafcf MJRefresh: ff9e531227924c84ce459338414550a05d2aea78 Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda @@ -99,6 +242,6 @@ SPEC CHECKSUMS: YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7 ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13 -PODFILE CHECKSUM: 25e7f44d27dd18aad94fde84cae1f0c157c60341 +PODFILE CHECKSUM: 12eed82b2517a28e085576b5b940ed72b5ab1b94 COCOAPODS: 1.16.2 diff --git a/ThimraTV.xcodeproj/project.pbxproj b/ThimraTV.xcodeproj/project.pbxproj index dc035fd..57e516b 100644 --- a/ThimraTV.xcodeproj/project.pbxproj +++ b/ThimraTV.xcodeproj/project.pbxproj @@ -267,11 +267,19 @@ 1BC1F0D72E0A35EF00B579A4 /* SPVideoRevolutionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BC1F0D62E0A35EF00B579A4 /* SPVideoRevolutionManager.swift */; }; 1BC1F0E32E0D268400B579A4 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 1BC1F0DC2E0D268400B579A4 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 1BC1F0EB2E0D268B00B579A4 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BC1F0E92E0D268B00B579A4 /* NotificationService.swift */; }; + 1BDE20132E1E15A700C2C2B5 /* SPAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BDE20122E1E159B00C2C2B5 /* SPAdManager.swift */; }; + 1BDE20172E1E164600C2C2B5 /* SPAdAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BDE20162E1E163E00C2C2B5 /* SPAdAPI.swift */; }; + 1BDE20192E1E175800C2C2B5 /* SPAdInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BDE20182E1E175800C2C2B5 /* SPAdInfo.swift */; }; + 1BDE201E2E1E3D3E00C2C2B5 /* SPRewardedAdManager+Admob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BDE201D2E1E384100C2C2B5 /* SPRewardedAdManager+Admob.swift */; }; + 1BDE20202E1E669600C2C2B5 /* AttributedString+SPAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BDE201F2E1E669000C2C2B5 /* AttributedString+SPAdd.swift */; }; 1BE7892B2DCB0E530001A8F1 /* FacebookCore in Frameworks */ = {isa = PBXBuildFile; productRef = 1BE7892A2DCB0E530001A8F1 /* FacebookCore */; }; 1BE7892D2DCB0E530001A8F1 /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 1BE7892C2DCB0E530001A8F1 /* FacebookLogin */; }; 1BF22FD12DC2169B0082429A /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 1BF22FD02DC2169B0082429A /* FirebaseAnalytics */; }; 1BF22FD32DC2169B0082429A /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 1BF22FD22DC2169B0082429A /* FirebaseCore */; }; 1BF22FD52DC2169B0082429A /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 1BF22FD42DC2169B0082429A /* FirebaseMessaging */; }; + 1BF5130C2E1F4660009750EA /* SPRewardedAdManager+AppLovin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5130B2E1F4654009750EA /* SPRewardedAdManager+AppLovin.swift */; }; + 1BF5130E2E1F5D9B009750EA /* SPRewardedAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */; }; + 1BF513112E1FA138009750EA /* SPStatAdModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513102E1FA138009750EA /* SPStatAdModel.swift */; }; C3D1CE788CA03A1878493356 /* Pods_ThimraTV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64805795B479324EB764157 /* Pods_ThimraTV.framework */; }; /* End PBXBuildFile section */ @@ -575,6 +583,14 @@ 1BC1F0DC2E0D268400B579A4 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 1BC1F0E82E0D268B00B579A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1BC1F0E92E0D268B00B579A4 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 1BDE20122E1E159B00C2C2B5 /* SPAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAdManager.swift; sourceTree = ""; }; + 1BDE20162E1E163E00C2C2B5 /* SPAdAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAdAPI.swift; sourceTree = ""; }; + 1BDE20182E1E175800C2C2B5 /* SPAdInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAdInfo.swift; sourceTree = ""; }; + 1BDE201D2E1E384100C2C2B5 /* SPRewardedAdManager+Admob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SPRewardedAdManager+Admob.swift"; sourceTree = ""; }; + 1BDE201F2E1E669000C2C2B5 /* AttributedString+SPAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+SPAdd.swift"; sourceTree = ""; }; + 1BF5130B2E1F4654009750EA /* SPRewardedAdManager+AppLovin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SPRewardedAdManager+AppLovin.swift"; sourceTree = ""; }; + 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPRewardedAdManager.swift; sourceTree = ""; }; + 1BF513102E1FA138009750EA /* SPStatAdModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPStatAdModel.swift; sourceTree = ""; }; 1DBC40592DA4EDFC0093FCB0 /* ThimraTV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThimraTV.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1F666DE0B12C863F26BE5027 /* Pods-MoviaBox.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoviaBox.debug.xcconfig"; path = "Target Support Files/Pods-MoviaBox/Pods-MoviaBox.debug.xcconfig"; sourceTree = ""; }; A1174E10BCF2C606F7818792 /* Pods-ThimraTV.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThimraTV.release.xcconfig"; path = "Target Support Files/Pods-ThimraTV/Pods-ThimraTV.release.xcconfig"; sourceTree = ""; }; @@ -656,6 +672,7 @@ 1BB91BDF2E04FD6A00A2C715 /* Extension */ = { isa = PBXGroup; children = ( + 1BDE201F2E1E669000C2C2B5 /* AttributedString+SPAdd.swift */, 1BB91BCC2E04FD6A00A2C715 /* CGMutablePath+SPAdd.swift */, 1BB91BCD2E04FD6A00A2C715 /* Date+SPAdd.swift */, 1BB91BCE2E04FD6A00A2C715 /* Dictionary+SPAdd.swift */, @@ -692,6 +709,7 @@ 1BB91BEC2E04FD6A00A2C715 /* API */ = { isa = PBXGroup; children = ( + 1BDE20162E1E163E00C2C2B5 /* SPAdAPI.swift */, 1BB91BE42E04FD6A00A2C715 /* SPApnsAPI.swift */, 1BB91BE52E04FD6A00A2C715 /* SPHomeAPI.swift */, 1BB91BE62E04FD6A00A2C715 /* SPRewardsAPI.swift */, @@ -1266,6 +1284,7 @@ 1BB91CD12E04FD6A00A2C715 /* Libs */ = { isa = PBXGroup; children = ( + 1BDE20112E1E158400C2C2B5 /* AdManager */, 1BB91CB22E04FD6A00A2C715 /* Alert */, 1BB91CB42E04FD6A00A2C715 /* APPTool */, 1BB91CB62E04FD6A00A2C715 /* Cache */, @@ -1458,6 +1477,27 @@ path = NotificationService; sourceTree = ""; }; + 1BDE20112E1E158400C2C2B5 /* AdManager */ = { + isa = PBXGroup; + children = ( + 1BF5130F2E1F5EE4009750EA /* RewardedAd */, + 1BDE20122E1E159B00C2C2B5 /* SPAdManager.swift */, + 1BDE20182E1E175800C2C2B5 /* SPAdInfo.swift */, + 1BF513102E1FA138009750EA /* SPStatAdModel.swift */, + ); + path = AdManager; + sourceTree = ""; + }; + 1BF5130F2E1F5EE4009750EA /* RewardedAd */ = { + isa = PBXGroup; + children = ( + 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */, + 1BDE201D2E1E384100C2C2B5 /* SPRewardedAdManager+Admob.swift */, + 1BF5130B2E1F4654009750EA /* SPRewardedAdManager+AppLovin.swift */, + ); + path = RewardedAd; + sourceTree = ""; + }; 1DBC40502DA4EDFC0093FCB0 = { isa = PBXGroup; children = ( @@ -1502,8 +1542,6 @@ dependencies = ( ); name = NotificationService; - packageProductDependencies = ( - ); productName = NotificationService; productReference = 1BC1F0DC2E0D268400B579A4 /* NotificationService.appex */; productType = "com.apple.product-type.app-extension"; @@ -1518,6 +1556,7 @@ 1DBC40572DA4EDFC0093FCB0 /* Resources */, 4E1CBF3F1205E28DFCF11722 /* [CP] Embed Pods Frameworks */, 1BB9206E2E050B1A00A2C715 /* Embed Foundation Extensions */, + D05FE8FEDBB2F36A4EF15C23 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1637,6 +1676,27 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + D05FE8FEDBB2F36A4EF15C23 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThimraTV/Pods-ThimraTV-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThimraTV/Pods-ThimraTV-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ThimraTV/Pods-ThimraTV-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1713,6 +1773,7 @@ 1BB91D492E04FD6A00A2C715 /* SPTextField.swift in Sources */, 1BB91D4A2E04FD6A00A2C715 /* SPCampaignWebViewController.swift in Sources */, 1BB91D4B2E04FD6A00A2C715 /* SPWebMessageModel.swift in Sources */, + 1BDE20192E1E175800C2C2B5 /* SPAdInfo.swift in Sources */, 1BB91D4C2E04FD6A00A2C715 /* SPWebView.swift in Sources */, 1BB91D4D2E04FD6A00A2C715 /* SPWebViewController.swift in Sources */, 1BB91D4E2E04FD6A00A2C715 /* SPWebViewController+ScriptMessage.swift in Sources */, @@ -1729,6 +1790,7 @@ 1BB91D592E04FD6A00A2C715 /* SPHomeChildController.swift in Sources */, 1BB91D5A2E04FD6A00A2C715 /* SPHomePageController.swift in Sources */, 1BB91D5B2E04FD6A00A2C715 /* SPHomeV2ViewController.swift in Sources */, + 1BDE201E2E1E3D3E00C2C2B5 /* SPRewardedAdManager+Admob.swift in Sources */, 1BB91D5C2E04FD6A00A2C715 /* SPHomeViewController.swift in Sources */, 1BB91D5D2E04FD6A00A2C715 /* SPSearchViewController.swift in Sources */, 1BB91D5E2E04FD6A00A2C715 /* SPHomeCategoryModel.swift in Sources */, @@ -1747,12 +1809,15 @@ 1BB91D6B2E04FD6A00A2C715 /* SPHomeHeaderView.swift in Sources */, 1BB91D6C2E04FD6A00A2C715 /* SPHomeHistoryContentCell.swift in Sources */, 1BB91D6D2E04FD6A00A2C715 /* SPHomeHotCell.swift in Sources */, + 1BF513112E1FA138009750EA /* SPStatAdModel.swift in Sources */, 1BB91D6E2E04FD6A00A2C715 /* SPHomeHotContentCell.swift in Sources */, 1BB91D6F2E04FD6A00A2C715 /* SPHomeHotSearchCell.swift in Sources */, 1BB91D702E04FD6A00A2C715 /* SPHomeHotSearchView.swift in Sources */, + 1BF5130C2E1F4660009750EA /* SPRewardedAdManager+AppLovin.swift in Sources */, 1BB91D712E04FD6A00A2C715 /* SPHomeHotView.swift in Sources */, 1BB91D722E04FD6A00A2C715 /* SPHomeNineSquareContentCell.swift in Sources */, 1BB91D732E04FD6A00A2C715 /* SPHomePlayHistoricalView.swift in Sources */, + 1BDE20172E1E164600C2C2B5 /* SPAdAPI.swift in Sources */, 1BB91D742E04FD6A00A2C715 /* SPHomePlayHistoryCell.swift in Sources */, 1BB91D752E04FD6A00A2C715 /* SPHomePlayHistoryView.swift in Sources */, 1BB91D762E04FD6A00A2C715 /* SPHomeSearchButton.swift in Sources */, @@ -1794,6 +1859,7 @@ 1BB91D9A2E04FD6A00A2C715 /* SPMineMemberYesView.swift in Sources */, 1BB91D9B2E04FD6A00A2C715 /* SPMinePlayHistoryCell.swift in Sources */, 1BB91D9C2E04FD6A00A2C715 /* SPMinePlayHistoryView.swift in Sources */, + 1BDE20202E1E669600C2C2B5 /* AttributedString+SPAdd.swift in Sources */, 1BB91D9D2E04FD6A00A2C715 /* SPMineWalletView.swift in Sources */, 1BB91D9E2E04FD6A00A2C715 /* SPSettingsCell.swift in Sources */, 1BB91D9F2E04FD6A00A2C715 /* SPCollectListViewController.swift in Sources */, @@ -1825,8 +1891,10 @@ 1BB91DB92E04FD6A00A2C715 /* SPSpeedSelectedView.swift in Sources */, 1BB91DBA2E04FD6A00A2C715 /* SPPlayerListViewModel.swift in Sources */, 1BB91DBB2E04FD6A00A2C715 /* SPRewardsViewController.swift in Sources */, + 1BDE20132E1E15A700C2C2B5 /* SPAdManager.swift in Sources */, 1BB91DBC2E04FD6A00A2C715 /* SPCoinOrderRecordViewController.swift in Sources */, 1BB91DBD2E04FD6A00A2C715 /* SPConsumptionRecordsViewController.swift in Sources */, + 1BF5130E2E1F5D9B009750EA /* SPRewardedAdManager.swift in Sources */, 1BB91DBE2E04FD6A00A2C715 /* SPOrderRecordsPageViewController.swift in Sources */, 1BB91DBF2E04FD6A00A2C715 /* SPRewardCoinsViewController.swift in Sources */, 1BB91DC02E04FD6A00A2C715 /* SPStoreViewController.swift in Sources */, diff --git a/ThimraTV/AppDelegate/AppDelegate+Thirdparty.swift b/ThimraTV/AppDelegate/AppDelegate+Thirdparty.swift index cbe61a5..d157436 100644 --- a/ThimraTV/AppDelegate/AppDelegate+Thirdparty.swift +++ b/ThimraTV/AppDelegate/AppDelegate+Thirdparty.swift @@ -18,6 +18,8 @@ extension AppDelegate { #if canImport(FacebookCore) ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions) #endif + ///初始化广告sdk + SPAdManager.manager.start() registAdjust() ///设置刷新控件的语言 diff --git a/ThimraTV/Base/Extension/AttributedString+SPAdd.swift b/ThimraTV/Base/Extension/AttributedString+SPAdd.swift new file mode 100644 index 0000000..ae94b85 --- /dev/null +++ b/ThimraTV/Base/Extension/AttributedString+SPAdd.swift @@ -0,0 +1,15 @@ +// +// AttributedString+SPAdd.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/9. +// + + +extension AttributedString { + + static func sp_create(text: String, color: UIColor, font: UIFont) -> AttributedString { + return AttributedString.init(text, attributes: AttributeContainer([.font : font, .foregroundColor : color])) + } + +} diff --git a/ThimraTV/Base/Networking/API/SPAdAPI.swift b/ThimraTV/Base/Networking/API/SPAdAPI.swift new file mode 100644 index 0000000..b89fb3b --- /dev/null +++ b/ThimraTV/Base/Networking/API/SPAdAPI.swift @@ -0,0 +1,39 @@ +// +// SPAdAPI.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/9. +// + +import UIKit + +class SPAdAPI { + + ///获取激励广告信息 + static func requestShowAdInfo(completer: ((_ adInfo: SPAdInfo?) -> Void)?) { + var param = SPNetworkParameters(path: "/ad/getShowAdInfo") + param.method = .get + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + completer?(response.data?.ad) + } + } + + ///广告解锁视频 + static func requestAdUnlockVideo(shortPlayId: String, videoId: String, adInfo: SPAdInfo, completer: ((_ model: SPVideoUnlockModel?) -> Void)?) { + + var param = SPNetworkParameters(path: "/viewAdsUnlockVideo") +// param.isLoding = true + param.parameters = [ + "short_play_id" : shortPlayId, + "video_id" : videoId, + "ads_id" : adInfo.ads_id ?? "", + "ads_platform_key" : adInfo.platform_key?.rawValue ?? "", + "trans_id" : adInfo.ads_id ?? "", + ] + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + completer?(response.data) + } + } +} diff --git a/ThimraTV/Base/Networking/API/SPStatAPI.swift b/ThimraTV/Base/Networking/API/SPStatAPI.swift index 4601f67..72071b5 100644 --- a/ThimraTV/Base/Networking/API/SPStatAPI.swift +++ b/ThimraTV/Base/Networking/API/SPStatAPI.swift @@ -72,4 +72,18 @@ class SPStatAPI: NSObject { } } + + ///广告统计 + static func requestStatAd(model: SPStatAdModel) { + + var param = SPNetworkParameters(path: "/ad/history") + param.isToast = false + param.isLoding = false + param.parameters = model.toDictionary() + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + + } + + } } diff --git a/ThimraTV/Class/Mine/Controller/SPMineViewController.swift b/ThimraTV/Class/Mine/Controller/SPMineViewController.swift index 6e0f203..7cfeee2 100644 --- a/ThimraTV/Class/Mine/Controller/SPMineViewController.swift +++ b/ThimraTV/Class/Mine/Controller/SPMineViewController.swift @@ -24,6 +24,9 @@ class SPMineViewController: SPViewController { ///是否进入过 private var isHaveEntered = false + ///是否需要展示激励广告 + private var needShowRewardedAd: Bool = false + weak var vipAlertView: SPVipAlertView? //MARK: UI 属性 private lazy var headerView: SPMineHeaderView = { @@ -69,8 +72,10 @@ class SPMineViewController: SPViewController { isHaveEntered = true + self.showRewardedAd() ///弹出VIP提示框 showVipAlert() + } private func updateHeaderView() { @@ -92,6 +97,7 @@ class SPMineViewController: SPViewController { extension SPMineViewController { private func showVipAlert() { + guard SPLoginManager.manager.userInfo?.user_level != .ad else { return } guard SPLoginManager.manager.userInfo?.is_vip != true else { return } guard SPVipAlertView.isShowAlert else { return } @@ -108,6 +114,14 @@ extension SPMineViewController { } } + private func showRewardedAd() { + guard SPLoginManager.manager.userInfo?.user_level == .ad else { return } + guard needShowRewardedAd else { return } + needShowRewardedAd = false + + SPRewardedAdManager.manager.loadAndShowRewardedAd() + } + } extension SPMineViewController { @@ -138,6 +152,8 @@ extension SPMineViewController: UITableViewDelegate, UITableViewDataSource { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + self.needShowRewardedAd = true + let item = dataArr[indexPath.row] switch item.type { case .privacyPolicy: diff --git a/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift b/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift index fe4dec9..b974c46 100644 --- a/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift +++ b/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift @@ -149,9 +149,8 @@ extension SPSettingsViewController { SPMineItem(type: .clearCache, title: "movia_Clear_cache".localized, subtitle: SPAppCacheManager.cacheToString(cache: cache)), SPMineItem(type: .aboutUs, title: "movia_profile_About_Us".localized), ] - + arr.append(SPMineItem(type: .deleteAccount, title: "DeleteAccount".localized)) if SPLoginManager.manager.isLogin { - arr.append(SPMineItem(type: .deleteAccount, title: "DeleteAccount".localized)) arr.append(SPMineItem(type: .logout, title: "movia_signout".localized)) } diff --git a/ThimraTV/Class/Mine/View/SPMineHeaderView.swift b/ThimraTV/Class/Mine/View/SPMineHeaderView.swift index b192520..02fbd4a 100644 --- a/ThimraTV/Class/Mine/View/SPMineHeaderView.swift +++ b/ThimraTV/Class/Mine/View/SPMineHeaderView.swift @@ -13,13 +13,18 @@ class SPMineHeaderView: UIView { var height: CGFloat = kSPStatusbarHeight + 100 var stackHeight = 0.0 - stackHeight += memberView.intrinsicContentSize.height - stackHeight += self.stackView.spacing - stackHeight += walletView.intrinsicContentSize.height + if userInfo?.user_level != .ad { + stackHeight += memberView.intrinsicContentSize.height + + stackHeight += self.stackView.spacing + stackHeight += walletView.intrinsicContentSize.height + } if playHistoryArr?.count ?? 0 > 0 { - stackHeight += self.stackView.spacing + if stackHeight > 0 { + stackHeight += self.stackView.spacing + } stackHeight += playHistoryView.contentHeight } @@ -49,6 +54,8 @@ class SPMineHeaderView: UIView { memberView.userInfo = self.userInfo walletView.userInfo = self.userInfo + + updateStackViewLayout() } } @@ -150,9 +157,15 @@ class SPMineHeaderView: UIView { private func updateStackViewLayout() { stackView.removeAllArrangedSubview() - stackView.addArrangedSubview(memberView) - - stackView.addArrangedSubview(walletView) + if userInfo?.user_level != .ad { + stackView.addArrangedSubview(memberView) + + stackView.addArrangedSubview(walletView) + + loginButton.isHidden = false + } else { + loginButton.isHidden = true + } if let arr = playHistoryArr, arr.count > 0 { stackView.addArrangedSubview(playHistoryView) diff --git a/ThimraTV/Class/Player/Controller/SPPlayerDetailViewController.swift b/ThimraTV/Class/Player/Controller/SPPlayerDetailViewController.swift index ea41728..b4714ee 100644 --- a/ThimraTV/Class/Player/Controller/SPPlayerDetailViewController.swift +++ b/ThimraTV/Class/Player/Controller/SPPlayerDetailViewController.swift @@ -253,42 +253,31 @@ extension SPPlayerDetailViewController { ///解锁视频 private func unlockVideo(indexPath: IndexPath) { - - guard let videoInfo = detailModel?.episodeList?[indexPath.row] else { return } - - guard let shortPlayId = videoInfo.short_play_id, let videoId = videoInfo.short_play_video_id else { return } - - SPWalletAPI.requestCoinUnlockVideo(shortPlayId: shortPlayId, videoId: videoId) { [weak self] model in + self.viewModel.unlockVideo { [weak self] finish in guard let self = self else { return } - guard let model = model else { return } - - switch model.status { - case .jump: - SPToast.show(text: "movia_jump_unlock_error".localized) - - case .noPlay: - SPToast.show(text: "movia_buy_fail_toast_01".localized) - - case .notEnough: - self.onPlayBuy() - - case .success: - videoInfo.is_lock = false - self.reloadData { [weak self] in - guard let self = self else { return } - self.play() - } - //更新用户信息 - SPLoginManager.manager.updateUserInfo(completer: nil) - - default: - break + if finish { + self.reloadData { [weak self] in + guard let self = self else { return } + self.play() + } } - - } } + ///广告解锁 + private func adUnlockVideo() { + self.viewModel.adUnlockVideo { [weak self] in + guard let self = self else { return } + + self.reloadData { [weak self] in + guard let self = self else { return } + self.play() + } + } + } + + + ///上报播放进度 private func uploadPlayTime() { let videoInfo = self.viewModel.currentPlayer?.videoInfo @@ -362,6 +351,10 @@ extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SP self.unlockVideo(indexPath: indexPath) } + cell.clickAdUnlockButton = { [weak self] cell in + self?.adUnlockVideo() + } + } return oldCell } diff --git a/ThimraTV/Class/Player/Controller/SPPlayerListViewController.swift b/ThimraTV/Class/Player/Controller/SPPlayerListViewController.swift index 0401e29..6e9ca8c 100644 --- a/ThimraTV/Class/Player/Controller/SPPlayerListViewController.swift +++ b/ThimraTV/Class/Player/Controller/SPPlayerListViewController.swift @@ -193,16 +193,18 @@ class SPPlayerListViewController: SPViewController { } func reloadData(completion: (() -> Void)? = nil) { - CATransaction.setCompletionBlock { [weak self] in + UIView.performWithoutAnimation { + self.collectionView.reloadData() + } + + self.collectionView.performBatchUpdates(nil) { [weak self] _ in guard let self = self else { return } let cell = self.collectionView.cellForItem(at: self.viewModel.currentIndexPath) as? SPPlayerListCell self.viewModel.currentPlayer = cell completion?() } - CATransaction.begin() - self.collectionView.reloadData() - CATransaction.commit() + } func getDataCount() -> Int { diff --git a/ThimraTV/Class/Player/View/SPPlayLockView.swift b/ThimraTV/Class/Player/View/SPPlayLockView.swift index ee11569..37d603e 100644 --- a/ThimraTV/Class/Player/View/SPPlayLockView.swift +++ b/ThimraTV/Class/Player/View/SPPlayLockView.swift @@ -9,6 +9,12 @@ import UIKit class SPPlayLockView: UIView { + ///点击解锁按钮 + var clickUnlockButton: (() -> Void)? + + ///点击广告解锁 + var clickAdUnlockButton: (() -> Void)? + ///是否是解锁上一集 var isUnlockUpEpisode: Bool = false { didSet { @@ -18,12 +24,22 @@ class SPPlayLockView: UIView { var videoInfo: SPVideoInfoModel? { didSet { - coinView.setTitle("\(videoInfo?.coins ?? 0)", for: .normal) + let userInfo = SPLoginManager.manager.userInfo + let coins = videoInfo?.coins ?? 0 + let myCoins = (userInfo?.coin_left_total ?? 0) + (userInfo?.send_coin_left_total ?? 0) + coinView.setTitle("\(coins)", for: .normal) + + + if SPLoginManager.manager.userInfo?.user_level == .ad && (coins == 0 || myCoins < coins) { + adUnlockButton.isHidden = false + unlockButton.isHidden = true + } else { + adUnlockButton.isHidden = true + unlockButton.isHidden = false + } } } - ///点击解锁按钮 - var clickUnlockButton: (() -> Void)? //MARK: UI属性 private lazy var containerView: UIView = { @@ -91,12 +107,38 @@ class SPPlayLockView: UIView { return view }() + private lazy var adUnlockButton: UIButton = { + var config = UIButton.Configuration.plain() + config.image = UIImage(named: "video_icon_01") + config.imagePadding = 6 + + let button = UIButton(configuration: config) + button.isHidden = true + button.layer.cornerRadius = 27 + button.layer.masksToBounds = true + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.colorFFFFFF().cgColor + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + if isUnlockUpEpisode { + button.configuration?.attributedTitle = AttributedString.sp_create(text: "movia_video_lock_tip_02".localized, color: .colorFFFFFF(), font: .fontMedium(ofSize: 16)) + } else { + button.configuration?.attributedTitle = AttributedString.sp_create(text: "movia_video_lock_tip_03".localized, color: .colorFFFFFF(), font: .fontMedium(ofSize: 16)) + } + } + button.addTarget(self, action: #selector(handleAdUnlockButton), for: .touchUpInside) + return button + }() + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .color000000(alpha: 0.75) updateUnlockButton() + + + _setupUI() } @@ -115,6 +157,15 @@ class SPPlayLockView: UIView { } + @objc private func handleAdUnlockButton() { + if isUnlockUpEpisode { + SPToast.show(text: "movia_jump_unlock_error".localized) + return + } + + self.clickAdUnlockButton?() + } + private func updateUnlockButton() { unlockStackView.removeAllArrangedSubview() @@ -129,6 +180,8 @@ class SPPlayLockView: UIView { unlockStackView.addArrangedSubview(coinView) } + + adUnlockButton.setNeedsUpdateConfiguration() } } @@ -141,6 +194,7 @@ extension SPPlayLockView { containerView.addSubview(lockTextLabel) containerView.addSubview(unlockButton) unlockButton.addSubview(unlockStackView) + containerView.addSubview(adUnlockButton) containerView.snp.makeConstraints { make in make.centerY.equalToSuperview() @@ -170,6 +224,9 @@ extension SPPlayLockView { make.centerX.equalToSuperview() } + adUnlockButton.snp.makeConstraints { make in + make.edges.equalTo(unlockButton) + } } } diff --git a/ThimraTV/Class/Player/View/SPPlayerDetailCell.swift b/ThimraTV/Class/Player/View/SPPlayerDetailCell.swift index 3ec7fda..fbad3ae 100644 --- a/ThimraTV/Class/Player/View/SPPlayerDetailCell.swift +++ b/ThimraTV/Class/Player/View/SPPlayerDetailCell.swift @@ -15,6 +15,8 @@ class SPPlayerDetailCell: SPPlayerListCell { ///点击解锁按钮 var clickUnlockButton: ((_ cell: SPPlayerDetailCell) -> Void)? + ///点击广告解锁 + var clickAdUnlockButton: ((_ cell: SPPlayerDetailCell) -> Void)? ///上一集是否加锁 var hasLockUpEpisode = false { @@ -32,6 +34,11 @@ class SPPlayerDetailCell: SPPlayerListCell { self.clickUnlockButton?(self) } + controlView.clickAdUnlockButton = { [weak self] in + guard let self = self else { return } + self.clickAdUnlockButton?(self) + } + } } diff --git a/ThimraTV/Class/Player/View/SPPlayerDetailControlView.swift b/ThimraTV/Class/Player/View/SPPlayerDetailControlView.swift index da30865..d08f99b 100644 --- a/ThimraTV/Class/Player/View/SPPlayerDetailControlView.swift +++ b/ThimraTV/Class/Player/View/SPPlayerDetailControlView.swift @@ -92,6 +92,8 @@ class SPPlayerDetailControlView: SPPlayerControlView { ///点击解锁按钮 var clickUnlockButton: (() -> Void)? + ///点击广告解锁 + var clickAdUnlockButton: (() -> Void)? ///暂停按钮倒计时 private var timer: Timer? @@ -150,6 +152,9 @@ class SPPlayerDetailControlView: SPPlayerControlView { view.clickUnlockButton = { [weak self] in self?.clickUnlockButton?() } + view.clickAdUnlockButton = { [weak self] in + self?.clickAdUnlockButton?() + } return view }() diff --git a/ThimraTV/Class/Player/ViewModel/SPPlayerListViewModel.swift b/ThimraTV/Class/Player/ViewModel/SPPlayerListViewModel.swift index ec4ae53..8e3de6c 100644 --- a/ThimraTV/Class/Player/ViewModel/SPPlayerListViewModel.swift +++ b/ThimraTV/Class/Player/ViewModel/SPPlayerListViewModel.swift @@ -33,6 +33,9 @@ class SPPlayerListViewModel: NSObject { @objc dynamic private(set) lazy var speedModel = SPSpeedModel(speed: .x1) + ///视频解锁成功 + private var videoUnlockFinishBlock: (() -> Void)? + ///设置倍速播放 func setSpeedPlay(speedModel: SPSpeedModel) { self.speedModel = speedModel @@ -88,7 +91,9 @@ extension SPPlayerListViewModel { ///打开支付页面 func onPlayBuy() { + let userInfo = SPLoginManager.manager.userInfo guard let videoInfo = self.currentPlayer?.videoInfo else { return } + guard userInfo?.user_level != .ad else { return } let view = SPPlayBuyView() view.shortPlayId = videoInfo.short_play_id @@ -101,4 +106,72 @@ extension SPPlayerListViewModel { view.present(in: nil) } + func unlockVideo(completer: ((_ finish: Bool) -> Void)?) { + let videoInfo = self.currentPlayer?.videoInfo + guard let shortPlayId = videoInfo?.short_play_id, let videoId = videoInfo?.short_play_video_id else { return } + + SPWalletAPI.requestCoinUnlockVideo(shortPlayId: shortPlayId, videoId: videoId) { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + + switch model.status { + case .jump: + SPToast.show(text: "movia_jump_unlock_error".localized) + completer?(false) + + case .noPlay: + SPToast.show(text: "movia_buy_fail_toast_01".localized) + completer?(false) + + case .notEnough: + self.onPlayBuy() + completer?(false) + + case .success: + videoInfo?.is_lock = false + completer?(true) + + //更新用户信息 + SPLoginManager.manager.updateUserInfo(completer: nil) + + default: + completer?(false) + break + } + } + } + + func adUnlockVideo(finish: (() -> Void)?) { + let manager = SPRewardedAdManager.manager + manager.delegate = self + manager.videoInfo = self.currentPlayer?.videoInfo + manager.statScene = .detail + manager.loadAndShowRewardedAd() + + self.videoUnlockFinishBlock = finish + } + +} + +//MARK: -------------- SPAdManagerDelegate -------------- +extension SPPlayerListViewModel: SPRewardedAdManagerDelegate { + + ///用户获得奖励 + func rewardedAdManager(manager: SPRewardedAdManager, userDidEarnReward adInfo: SPAdInfo) { + let videoInfo = self.currentPlayer?.videoInfo + guard let shortPlayId = videoInfo?.short_play_id, let videoId = videoInfo?.short_play_video_id else { return } + + SPAdAPI.requestAdUnlockVideo(shortPlayId: shortPlayId, videoId: videoId, adInfo: adInfo) { [weak self] model in + guard let self = self else { return } + + if model?.status == .jump { + SPToast.show(text: "movia_jump_unlock_error".localized) + + } else if model?.status == .success { + self.currentPlayer?.videoInfo?.is_lock = false + self.videoUnlockFinishBlock?() + + } + } + } } diff --git a/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager+AppLovin.swift b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager+AppLovin.swift new file mode 100644 index 0000000..ae5b0b2 --- /dev/null +++ b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager+AppLovin.swift @@ -0,0 +1,160 @@ +// +// SPAdManager+AppLovin.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/10. +// + +#if canImport(AppLovinSDK) +import AppLovinSDK +#endif + + +extension SPRewardedAdManager { + + var appLovin_adUnitID: String { + return "ca-app-pub-3940256099942544/1712485313" + } + + fileprivate struct AssociatedKeys { + static var appLovin_rewardedAd: Int? + static var appLovin_needShowRewardedAd: Int? + static var appLovin_isLoadingRewardedAd: Int? + static var appLovin_retryAttempt: Int? + } + +#if canImport(AppLovinSDK) + private var appLovin_rewardedAd: MARewardedAd? { + set { + objc_setAssociatedObject(self, &AssociatedKeys.appLovin_rewardedAd, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + get { + return objc_getAssociatedObject(self, &AssociatedKeys.appLovin_rewardedAd) as? MARewardedAd + } + } + + private var appLovin_retryAttempt: CGFloat { + set { + objc_setAssociatedObject(self, &AssociatedKeys.appLovin_retryAttempt, newValue, .OBJC_ASSOCIATION_ASSIGN) + } + get { + return (objc_getAssociatedObject(self, &AssociatedKeys.appLovin_retryAttempt) as? CGFloat) ?? 0 + } + } + + ///是否需要展示广告,true广告加载完成会直接展示广告 + private var appLovin_needShowRewardedAd: Bool { + set { + objc_setAssociatedObject(self, &AssociatedKeys.appLovin_needShowRewardedAd, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + get { + return (objc_getAssociatedObject(self, &AssociatedKeys.appLovin_needShowRewardedAd) as? Bool) ?? false + } + } + + ///广告是否在加载中 + var appLovin_isLoadingRewardedAd: Bool { + set { + objc_setAssociatedObject(self, &AssociatedKeys.appLovin_isLoadingRewardedAd, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + get { + return (objc_getAssociatedObject(self, &AssociatedKeys.appLovin_isLoadingRewardedAd) as? Bool) ?? false + } + } +#endif + + ///加载激励广告 + func appLovin_loadRewardedAd() { +#if canImport(AppLovinSDK) + if !appLovin_isLoadingRewardedAd { + self.appLovin_isLoadingRewardedAd = true + + appLovin_rewardedAd = MARewardedAd.shared(withAdUnitIdentifier: appLovin_adUnitID) + appLovin_rewardedAd?.delegate = self + appLovin_rewardedAd?.load() + } +#endif + } + + ///加载并展示广告 + func appLovin_loadAndShowRewardedAd() { +#if canImport(AppLovinSDK) + self.appLovin_needShowRewardedAd = true + + if appLovin_rewardedAd?.isReady == true { + self.appLovin_show() + } else { + appLovin_loadRewardedAd() + } +#endif + } + + ///展示广告 + private func appLovin_show() { +#if canImport(AppLovinSDK) + if appLovin_rewardedAd?.isReady == true, self.appLovin_needShowRewardedAd { + self.appLovin_needShowRewardedAd = false + appLovin_rewardedAd?.show() + } + +#endif + } + + +} + +#if canImport(AppLovinSDK) +extension SPRewardedAdManager: MARewardedAdDelegate { + + func didLoad(_ ad: MAAd) + { + self.appLovin_isLoadingRewardedAd = false + // Rewarded ad is ready to show. '[self.rewardedAd isReady]' now returns 'YES'. + + // Reset retry attempt + appLovin_retryAttempt = 0 + + appLovin_show() + } + + func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) + { + // Rewarded ad failed to load + // AppLovin recommends that you retry with exponentially higher delays up to a maximum delay (in this case 64 seconds). + + appLovin_retryAttempt += 1 + let delaySec = pow(2.0, min(6.0, appLovin_retryAttempt)) + + DispatchQueue.main.asyncAfter(deadline: .now() + delaySec) { + self.appLovin_rewardedAd?.load() + } + } + + func didDisplay(_ ad: MAAd) {} + + func didClick(_ ad: MAAd) {} + + func didHide(_ ad: MAAd) + { + // Rewarded ad is hidden. Pre-load the next ad + self.appLovin_isLoadingRewardedAd = false + appLovin_loadRewardedAd() + } + + func didFail(toDisplay ad: MAAd, withError error: MAError) + { + // Rewarded ad failed to display. AppLovin recommends that you load the next ad. + self.appLovin_isLoadingRewardedAd = false + appLovin_loadRewardedAd() + } + + // MARK: MARewardedAdDelegate Protocol + + func didRewardUser(for ad: MAAd, with reward: MAReward) + { + // Rewarded ad was displayed and user should receive the reward + } + + +} +#endif diff --git a/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager.swift b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager.swift new file mode 100644 index 0000000..5782063 --- /dev/null +++ b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager.swift @@ -0,0 +1,247 @@ +// +// SPRewardedAdManager.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/10. +// + +import UIKit + +@objc protocol SPRewardedAdManagerDelegate: NSObjectProtocol { + ///发放奖励 + @objc optional func rewardedAdManager(manager: SPRewardedAdManager, userDidEarnReward adInfo: SPAdInfo) + ///广告加载失败 + @objc optional func rewardedAdManager(manager: SPRewardedAdManager, didLoadFail error: Error) + ///广告加载成功 + @objc optional func rewardedAdManagerDidLoadFinish(manager: SPRewardedAdManager) + ///广告展示失败 + @objc optional func rewardedAdManager(manager: SPRewardedAdManager, didDisplayFail error: Error) + ///广告被展示 + @objc optional func rewardedAdManagerDidShow(manager: SPRewardedAdManager) + ///广告被关闭 + @objc optional func rewardedAdManagerDidDismiss(manager: SPRewardedAdManager) +} + +class SPRewardedAdManager: NSObject { + + static let manager = SPRewardedAdManager() + + weak var delegate: SPRewardedAdManagerDelegate? + + private(set) var isLoadingRewardedAd = false + + ///重试等待时长 + private var retryAttempt = 0.0 + ///重试次数 + private var retryCount = 0 + ///最大重试次数 + private let retryMaxCount = 1 + + private(set) var adInfo: SPAdInfo? + + private(set) var isShowLoading = false + private(set) var isShowToast = false + + private var adsDate: Date? + ///统计场景 + var statScene: SPStatAdModel.AdScene? + var videoInfo: SPVideoInfoModel? + + deinit { + NotificationCenter.default.removeObserver(self) + } + + override init() { + super.init() + NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil) + } + + ///加载并展示激励广告 + func loadAndShowRewardedAd() { + isShowLoading = true + isShowToast = true + + SPHUD.show() + + if let adInfo = adInfo {//已有广告,并且加载完成 + if adInfo.platform_key == .google { + self.admob_loadAndShowRewardedAd(adInfo: adInfo) + } + + } else { + SPAdAPI.requestShowAdInfo { [weak self] adInfo in + guard let self = self else { return } + guard let adInfo = adInfo else { + SPHUD.dismiss() + SPToast.show(text: "movia_no_ads_tip".localized) + return + } + self.adInfo = adInfo + if adInfo.platform_key == .google { + self.admob_loadAndShowRewardedAd(adInfo: adInfo) + } else { + self.adInfo = nil + } + } + } + + } + + ///预加载一个广告 + func preloadRewardedAd() { + guard !isLoadingRewardedAd else { return } + isShowLoading = false + isShowToast = false + + self.isLoadingRewardedAd = true + + SPAdAPI.requestShowAdInfo { [weak self] adInfo in + guard let self = self else { return } + guard let adInfo = adInfo else { + self.isLoadingRewardedAd = false + return + } + self.adInfo = adInfo + if adInfo.platform_key == .google { + self.admob_loadRewardedAd(adInfo: adInfo) + } else { + self.adInfo = nil + } + } + } + + ///重试加载广告 + private func retryLoadAd() { + guard retryCount < retryMaxCount else { + retryCount = 0 + retryAttempt = 0 + return + } + + retryCount += 1 + retryAttempt += 1 + let delaySec = pow(2.0, min(6.0, retryAttempt)) + + DispatchQueue.main.asyncAfter(deadline: .now() + delaySec) { [weak self] in + self?.preloadRewardedAd() + } + } + +} + + +//MARK: -------------- 广告内部调用(事件回调) -------------- +extension SPRewardedAdManager { + + ///奖励发放回调 + func userDidEarnRewardHandler() { + guard let adInfo = adInfo else { return } + self.delegate?.rewardedAdManager?(manager: self, userDidEarnReward: adInfo) + self.requestStatAd(type: "reward", errorMsg: nil) + } + + ///广告加载失败 + func loadFailHandler(error: Error) { + + if isShowLoading { + SPHUD.dismiss() + } + if isShowToast { + SPToast.show(text: "movia_no_ads_tip".localized) + } + self.isLoadingRewardedAd = false + self.delegate?.rewardedAdManager?(manager: self, didLoadFail: error) + self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription) + + self.statScene = nil + self.videoInfo = nil + self.retryLoadAd() + + } + + ///广告加载成功 + func loadFinishHandler() { + self.retryAttempt = 0 + self.retryCount = 0 + self.isLoadingRewardedAd = false + self.delegate?.rewardedAdManagerDidLoadFinish?(manager: self) + } + + ///广告展示失败 + func displayFailHandler(error: Error) { + if isShowLoading { + SPHUD.dismiss() + } + if isShowToast { + SPToast.show(text: "movia_no_ads_tip".localized) + } + self.delegate?.rewardedAdManager?(manager: self, didDisplayFail: error) + self.requestStatAd(type: "show_failed", errorMsg: error.localizedDescription) + + self.statScene = nil + self.videoInfo = nil + } + + ///广告被展示 + func didShowHandler() { + self.adsDate = Date() + + if isShowLoading { + SPHUD.dismiss() + } + self.delegate?.rewardedAdManagerDidShow?(manager: self) + + self.requestStatAd(type: "start", errorMsg: nil) + } + ///广告被关闭 + func didDismissHandler() { + self.delegate?.rewardedAdManagerDidDismiss?(manager: self) + + var seconds = 0 + if let adsDate = self.adsDate { + seconds = Int(Date().timeIntervalSince(adsDate)) + } + self.requestStatAd(type: "close", seconds: seconds, errorMsg: nil) + + self.statScene = nil + self.videoInfo = nil + preloadRewardedAd() + + } + + ///广告被点击 + func didClickHandler() { + var seconds = 0 + if let adsDate = self.adsDate { + seconds = Int(Date().timeIntervalSince(adsDate)) + } + + self.requestStatAd(type: "click", seconds: seconds, errorMsg: nil) + } + +} + +//MARK: -------------- 统计 -------------- +extension SPRewardedAdManager { + + private func requestStatAd(type: String, seconds: Int = 0, errorMsg: String?) { + guard let adInfo = adInfo else { return } + + let model = SPStatAdModel() + model.type = type + model.ads_id = adInfo.ads_id + model.view_seconds = seconds + model.ad_platform_key = adInfo.platform_key + model.error_msg = errorMsg + model.scene = self.statScene + model.short_play_id = self.videoInfo?.short_play_id + model.short_play_video_id = self.videoInfo?.short_play_video_id + + SPStatAPI.requestStatAd(model: model) + } + + + @objc private func didEnterBackgroundNotification() { + self.requestStatAd(type: "Interrupt", seconds: 0, errorMsg: nil) + } +} diff --git a/ThimraTV/Libs/AdManager/SPAdInfo.swift b/ThimraTV/Libs/AdManager/SPAdInfo.swift new file mode 100644 index 0000000..83d7aab --- /dev/null +++ b/ThimraTV/Libs/AdManager/SPAdInfo.swift @@ -0,0 +1,29 @@ +// +// SPAdInfo.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/9. +// + +import UIKit +import SmartCodable + + +enum SPAdPlatformKey: String, SmartCaseDefaultable { + case google = "google" +} + +class SPAdDataModel: SPModel, SmartCodable { + var ad: SPAdInfo? +} + +class SPAdInfo: SPModel, SmartCodable { + + + var id: String? + var platform_name: String? + var ads_id: String? + var status: String? + var platform_key: SPAdPlatformKey? + +} diff --git a/ThimraTV/Libs/AdManager/SPAdManager.swift b/ThimraTV/Libs/AdManager/SPAdManager.swift new file mode 100644 index 0000000..6e137d8 --- /dev/null +++ b/ThimraTV/Libs/AdManager/SPAdManager.swift @@ -0,0 +1,45 @@ +// +// SPAdManager.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/9. +// + +import UIKit +import GoogleMobileAds +#if canImport(AppLovinSDK) +import AppLovinSDK +#endif + + + +class SPAdManager: NSObject { + + static let manager = SPAdManager() + + func start() { + //初始化admob + MobileAds.shared.start() + + +#if canImport(AppLovinSDK) + //初始化 +// let initConfig = ALSdkInitializationConfiguration(sdkKey: "«SDK-key»") { builder in +// builder.mediationProvider = ALMediationProviderMAX +// } +// +// // Initialize the SDK with the configuration +// ALSdk.shared().initialize(with: initConfig) { sdkConfig in +// // Start loading ads +// } +#endif + //预加载一个激励广告 + SPRewardedAdManager.manager.preloadRewardedAd() + } + + + +} + + + diff --git a/ThimraTV/Libs/AdManager/SPStatAdModel.swift b/ThimraTV/Libs/AdManager/SPStatAdModel.swift new file mode 100644 index 0000000..52c4db9 --- /dev/null +++ b/ThimraTV/Libs/AdManager/SPStatAdModel.swift @@ -0,0 +1,25 @@ +// +// SPStatAdModel.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/10. +// + +import UIKit +import SmartCodable + +class SPStatAdModel: SPModel, SmartCodable { + + enum AdScene: String, SmartCaseDefaultable { + case detail = "detail" + } + + var type: String? //start click error click show_failed load_failed Interrupt(退到后台) close + var ads_id: String? + var view_seconds: Int? + var ad_platform_key: SPAdPlatformKey? + var scene: AdScene? // splash reward banner detail me turntable + var short_play_id: String? + var short_play_video_id: String? + var error_msg: String? +} diff --git a/ThimraTV/Libs/HUD/SPHUD.swift b/ThimraTV/Libs/HUD/SPHUD.swift index 1cd81b5..547b08c 100644 --- a/ThimraTV/Libs/HUD/SPHUD.swift +++ b/ThimraTV/Libs/HUD/SPHUD.swift @@ -12,9 +12,10 @@ class SPHUD: NSObject { SVProgressHUD.setDefaultMaskType(.clear) } - static func show() { + static func show(status: String? = nil) { SVProgressHUD.setDefaultMaskType(.clear) - SVProgressHUD.show() +// SVProgressHUD.show() + SVProgressHUD.show(withStatus: status) } static func dismiss() { diff --git a/ThimraTV/Libs/User/SPUserInfo.swift b/ThimraTV/Libs/User/SPUserInfo.swift index d0ca869..3ad5cea 100644 --- a/ThimraTV/Libs/User/SPUserInfo.swift +++ b/ThimraTV/Libs/User/SPUserInfo.swift @@ -10,6 +10,11 @@ import SmartCodable class SPUserInfo: SPModel, SmartCodable, NSSecureCoding { + enum UserLevel: String, SmartCaseDefaultable { + case normal = "normal" + case ad = "ad" + } + var id: String? var customer_id: String? @@ -28,7 +33,7 @@ class SPUserInfo: SPModel, SmartCodable, NSSecureCoding { var third_access_platform: String? var ip_address: String? var country_code: String? - var user_level: String? + var user_level: UserLevel? var avator: String? var sign_in_status: String? var registered_days: String? diff --git a/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Contents.json b/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Contents.json new file mode 100644 index 0000000..6a4d508 --- /dev/null +++ b/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Vector@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Vector@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Vector@2x.png b/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Vector@2x.png new file mode 100644 index 0000000..1f347b3 Binary files /dev/null and b/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Vector@2x.png differ diff --git a/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Vector@3x.png b/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Vector@3x.png new file mode 100644 index 0000000..4aefdde Binary files /dev/null and b/ThimraTV/Source/Assets.xcassets/icon/video_icon_01.imageset/Vector@3x.png differ diff --git a/ThimraTV/Source/Info.plist b/ThimraTV/Source/Info.plist index 4e93b72..be9551c 100644 --- a/ThimraTV/Source/Info.plist +++ b/ThimraTV/Source/Info.plist @@ -29,11 +29,628 @@ $(PRODUCT_NAME) FirebaseAppDelegateProxyEnabled + GADApplicationIdentifier + ca-app-pub-3940256099942544~1458002511 LSApplicationQueriesSchemes fbapi fb-messenger-share-api + SKAdNetworkItems + + + SKAdNetworkIdentifier + 22mmun2rn5.skadnetwork + + + SKAdNetworkIdentifier + 238da6jt44.skadnetwork + + + SKAdNetworkIdentifier + 24t9a8vw3c.skadnetwork + + + SKAdNetworkIdentifier + 24zw6aqk47.skadnetwork + + + SKAdNetworkIdentifier + 252b5q8x7y.skadnetwork + + + SKAdNetworkIdentifier + 275upjj5gd.skadnetwork + + + SKAdNetworkIdentifier + 294l99pt4k.skadnetwork + + + SKAdNetworkIdentifier + 2fnua5tdw4.skadnetwork + + + SKAdNetworkIdentifier + 2u9pt9hc89.skadnetwork + + + SKAdNetworkIdentifier + 32z4fx6l9h.skadnetwork + + + SKAdNetworkIdentifier + 3l6bd9hu43.skadnetwork + + + SKAdNetworkIdentifier + 3qcr597p9d.skadnetwork + + + SKAdNetworkIdentifier + 3qy4746246.skadnetwork + + + SKAdNetworkIdentifier + 3rd42ekr43.skadnetwork + + + SKAdNetworkIdentifier + 3sh42y64q3.skadnetwork + + + SKAdNetworkIdentifier + 424m5254lk.skadnetwork + + + SKAdNetworkIdentifier + 4468km3ulz.skadnetwork + + + SKAdNetworkIdentifier + 44jx6755aq.skadnetwork + + + SKAdNetworkIdentifier + 44n7hlldy6.skadnetwork + + + SKAdNetworkIdentifier + 47vhws6wlr.skadnetwork + + + SKAdNetworkIdentifier + 488r3q3dtq.skadnetwork + + + SKAdNetworkIdentifier + 4dzt52r2t5.skadnetwork + + + SKAdNetworkIdentifier + 4fzdc2evr5.skadnetwork + + + SKAdNetworkIdentifier + 4mn522wn87.skadnetwork + + + SKAdNetworkIdentifier + 4pfyvq9l8r.skadnetwork + + + SKAdNetworkIdentifier + 4w7y6s5ca2.skadnetwork + + + SKAdNetworkIdentifier + 523jb4fst2.skadnetwork + + + SKAdNetworkIdentifier + 52fl2v3hgk.skadnetwork + + + SKAdNetworkIdentifier + 54nzkqm89y.skadnetwork + + + SKAdNetworkIdentifier + 578prtvx9j.skadnetwork + + + SKAdNetworkIdentifier + 5a6flpkh64.skadnetwork + + + SKAdNetworkIdentifier + 5l3tpt7t6e.skadnetwork + + + SKAdNetworkIdentifier + 5lm9lj6jb7.skadnetwork + + + SKAdNetworkIdentifier + 5tjdwbrq8w.skadnetwork + + + SKAdNetworkIdentifier + 6964rsfnh4.skadnetwork + + + SKAdNetworkIdentifier + 6g9af3uyq4.skadnetwork + + + SKAdNetworkIdentifier + 6p4ks3rnbw.skadnetwork + + + SKAdNetworkIdentifier + 6v7lgmsu45.skadnetwork + + + SKAdNetworkIdentifier + 6xzpu9s2p8.skadnetwork + + + SKAdNetworkIdentifier + 737z793b9f.skadnetwork + + + SKAdNetworkIdentifier + 74b6s63p6l.skadnetwork + + + SKAdNetworkIdentifier + 79pbpufp6p.skadnetwork + + + SKAdNetworkIdentifier + 7fmhfwg9en.skadnetwork + + + SKAdNetworkIdentifier + 7rz58n8ntl.skadnetwork + + + SKAdNetworkIdentifier + 7ug5zh24hu.skadnetwork + + + SKAdNetworkIdentifier + 84993kbrcf.skadnetwork + + + SKAdNetworkIdentifier + 89z7zv988g.skadnetwork + + + SKAdNetworkIdentifier + 8c4e2ghe7u.skadnetwork + + + SKAdNetworkIdentifier + 8m87ys6875.skadnetwork + + + SKAdNetworkIdentifier + 8r8llnkz5a.skadnetwork + + + SKAdNetworkIdentifier + 8s468mfl3y.skadnetwork + + + SKAdNetworkIdentifier + 97r2b46745.skadnetwork + + + SKAdNetworkIdentifier + 9b89h5y424.skadnetwork + + + SKAdNetworkIdentifier + 9nlqeag3gk.skadnetwork + + + SKAdNetworkIdentifier + 9rd848q2bz.skadnetwork + + + SKAdNetworkIdentifier + 9t245vhmpl.skadnetwork + + + SKAdNetworkIdentifier + 9vvzujtq5s.skadnetwork + + + SKAdNetworkIdentifier + 9yg77x724h.skadnetwork + + + SKAdNetworkIdentifier + a2p9lx4jpn.skadnetwork + + + SKAdNetworkIdentifier + a7xqa6mtl2.skadnetwork + + + SKAdNetworkIdentifier + a8cz6cu7e5.skadnetwork + + + SKAdNetworkIdentifier + av6w8kgt66.skadnetwork + + + SKAdNetworkIdentifier + b9bk5wbcq9.skadnetwork + + + SKAdNetworkIdentifier + bxvub5ada5.skadnetwork + + + SKAdNetworkIdentifier + c3frkrj4fj.skadnetwork + + + SKAdNetworkIdentifier + c6k4g5qg8m.skadnetwork + + + SKAdNetworkIdentifier + cg4yq2srnc.skadnetwork + + + SKAdNetworkIdentifier + cj5566h2ga.skadnetwork + + + SKAdNetworkIdentifier + cp8zw746q7.skadnetwork + + + SKAdNetworkIdentifier + cs644xg564.skadnetwork + + + SKAdNetworkIdentifier + cstr6suwn9.skadnetwork + + + SKAdNetworkIdentifier + dbu4b84rxf.skadnetwork + + + SKAdNetworkIdentifier + dkc879ngq3.skadnetwork + + + SKAdNetworkIdentifier + dzg6xy7pwj.skadnetwork + + + SKAdNetworkIdentifier + e5fvkxwrpn.skadnetwork + + + SKAdNetworkIdentifier + ecpz2srf59.skadnetwork + + + SKAdNetworkIdentifier + eh6m2bh4zr.skadnetwork + + + SKAdNetworkIdentifier + ejvt5qm6ak.skadnetwork + + + SKAdNetworkIdentifier + f38h382jlk.skadnetwork + + + SKAdNetworkIdentifier + f73kdq92p3.skadnetwork + + + SKAdNetworkIdentifier + f7s53z58qe.skadnetwork + + + SKAdNetworkIdentifier + feyaarzu9v.skadnetwork + + + SKAdNetworkIdentifier + g28c52eehv.skadnetwork + + + SKAdNetworkIdentifier + g2y4y55b64.skadnetwork + + + SKAdNetworkIdentifier + g6gcrrvk4p.skadnetwork + + + SKAdNetworkIdentifier + ggvn48r87g.skadnetwork + + + SKAdNetworkIdentifier + glqzh8vgby.skadnetwork + + + SKAdNetworkIdentifier + gta8lk7p23.skadnetwork + + + SKAdNetworkIdentifier + gta9lk7p23.skadnetwork + + + SKAdNetworkIdentifier + hb56zgv37p.skadnetwork + + + SKAdNetworkIdentifier + hdw39hrw9y.skadnetwork + + + SKAdNetworkIdentifier + hs6bdukanm.skadnetwork + + + SKAdNetworkIdentifier + k674qkevps.skadnetwork + + + SKAdNetworkIdentifier + kbd757ywx3.skadnetwork + + + SKAdNetworkIdentifier + kbmxgpxpgc.skadnetwork + + + SKAdNetworkIdentifier + klf5c3l5u5.skadnetwork + + + SKAdNetworkIdentifier + krvm3zuq6h.skadnetwork + + + SKAdNetworkIdentifier + lr83yxwka7.skadnetwork + + + SKAdNetworkIdentifier + ludvb6z3bs.skadnetwork + + + SKAdNetworkIdentifier + m297p6643m.skadnetwork + + + SKAdNetworkIdentifier + m5mvw97r93.skadnetwork + + + SKAdNetworkIdentifier + m8dbw4sv7c.skadnetwork + + + SKAdNetworkIdentifier + mj797d8u6f.skadnetwork + + + SKAdNetworkIdentifier + mlmmfzh3r3.skadnetwork + + + SKAdNetworkIdentifier + mls7yz5dvl.skadnetwork + + + SKAdNetworkIdentifier + mp6xlyr22a.skadnetwork + + + SKAdNetworkIdentifier + mqn7fxpca7.skadnetwork + + + SKAdNetworkIdentifier + mtkv5xtk9e.skadnetwork + + + SKAdNetworkIdentifier + n38lu8286q.skadnetwork + + + SKAdNetworkIdentifier + n66cz3y3bx.skadnetwork + + + SKAdNetworkIdentifier + n6fk4nfna4.skadnetwork + + + SKAdNetworkIdentifier + n9x2a789qt.skadnetwork + + + SKAdNetworkIdentifier + ns5j362hk7.skadnetwork + + + SKAdNetworkIdentifier + nzq8sh4pbs.skadnetwork + + + SKAdNetworkIdentifier + p78axxw29g.skadnetwork + + + SKAdNetworkIdentifier + ppxm28t8ap.skadnetwork + + + SKAdNetworkIdentifier + prcb7njmu6.skadnetwork + + + SKAdNetworkIdentifier + pwa73g5rt2.skadnetwork + + + SKAdNetworkIdentifier + pwdxu55a5a.skadnetwork + + + SKAdNetworkIdentifier + qqp299437r.skadnetwork + + + SKAdNetworkIdentifier + qu637u8glc.skadnetwork + + + SKAdNetworkIdentifier + r45fhb6rf7.skadnetwork + + + SKAdNetworkIdentifier + rvh3l7un93.skadnetwork + + + SKAdNetworkIdentifier + rx5hdcabgc.skadnetwork + + + SKAdNetworkIdentifier + s39g8k73mm.skadnetwork + + + SKAdNetworkIdentifier + s69wq72ugq.skadnetwork + + + SKAdNetworkIdentifier + su67r6k2v3.skadnetwork + + + SKAdNetworkIdentifier + t38b2kh725.skadnetwork + + + SKAdNetworkIdentifier + tl55sbb4fm.skadnetwork + + + SKAdNetworkIdentifier + u679fj5vs4.skadnetwork + + + SKAdNetworkIdentifier + uw77j35x4d.skadnetwork + + + SKAdNetworkIdentifier + v4nxqhlyqp.skadnetwork + + + SKAdNetworkIdentifier + v72qych5uu.skadnetwork + + + SKAdNetworkIdentifier + v79kvwwj4g.skadnetwork + + + SKAdNetworkIdentifier + v9wttpbfk9.skadnetwork + + + SKAdNetworkIdentifier + vcra2ehyfk.skadnetwork + + + SKAdNetworkIdentifier + vhf287vqwu.skadnetwork + + + SKAdNetworkIdentifier + vutu7akeur.skadnetwork + + + SKAdNetworkIdentifier + w9q455wk68.skadnetwork + + + SKAdNetworkIdentifier + wg4vff78zm.skadnetwork + + + SKAdNetworkIdentifier + wzmmz9fp6w.skadnetwork + + + SKAdNetworkIdentifier + x44k69ngh6.skadnetwork + + + SKAdNetworkIdentifier + x5l83yy675.skadnetwork + + + SKAdNetworkIdentifier + x8jxxk4ff5.skadnetwork + + + SKAdNetworkIdentifier + x8uqf25wch.skadnetwork + + + SKAdNetworkIdentifier + xga6mpmplv.skadnetwork + + + SKAdNetworkIdentifier + xy9t38ct57.skadnetwork + + + SKAdNetworkIdentifier + y45688jllp.skadnetwork + + + SKAdNetworkIdentifier + y5ghdn5j9k.skadnetwork + + + SKAdNetworkIdentifier + yclnxrl5pm.skadnetwork + + + SKAdNetworkIdentifier + ydx93a7ass.skadnetwork + + + SKAdNetworkIdentifier + zmvfpc5aq8.skadnetwork + + + SKAdNetworkIdentifier + zq492l623r.skadnetwork + + UIAppFonts Inter-ExtraBold-5.otf diff --git a/ThimraTV/Source/en.lproj/Localizable.strings b/ThimraTV/Source/en.lproj/Localizable.strings index 2974903..e0b6838 100644 --- a/ThimraTV/Source/en.lproj/Localizable.strings +++ b/ThimraTV/Source/en.lproj/Localizable.strings @@ -87,6 +87,7 @@ "movia_purchase_VIP" = "Purchase VIP"; "movia_video_lock_tip_01" = "This episode is locked"; "movia_video_lock_tip_02" = "Please unlock the previous episode"; +"movia_video_lock_tip_03" = "Unlock watching an ad"; "movia_unlock_now_for" = "Unlock now for"; "movia_Purchase_Single_Episode" = "Purchase Single Episode"; "movia_Retry" = "Retry"; @@ -125,6 +126,7 @@ "movia_updatenowtitle" = "Discover a new version"; "movia_updatenow" = "Update Now"; "movia_no_short_found" = "No Short Found"; +"movia_no_ads_tip" = "Ad is on the way"; "movia_vip_alert_text_01" = "Short Drama VIP Exclusive";