Browse Source

Webview

master
marcoschmickler 4 years ago
parent
commit
b7aa687a53
  1. 24
      kplayer.xcodeproj/project.pbxproj
  2. 2
      kplayer/core/MediaItem.swift
  3. 64
      kplayer/detail/BrowserController.swift
  4. 22
      kplayer/detail/DetailViewController+Show.swift
  5. 4
      kplayer/video/SVideoModel.swift
  6. 266
      kplayer/web/KBrowserView.swift
  7. 189
      kplayer/web/WebView.swift
  8. 26
      kplayer/web/WebViewModel.swift

24
kplayer.xcodeproj/project.pbxproj

@ -10,6 +10,7 @@
1C73600CB93F16F4F28C116F /* KSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736A6E8396EE306B1AD3A8 /* KSettingsView.swift */; };
1C736048BFA120F5C7D36874 /* readme.md in Sources */ = {isa = PBXBuildFile; fileRef = 1C73685B4BBFDAFBF08C032C /* readme.md */; };
1C7360C0F2A4F0214FE353BD /* FileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7367ECBD369A2A0C94C499 /* FileHelper.swift */; };
1C736110BE0903CEFE5C18A4 /* download.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C7363A2B3338DAF752D8CE9 /* download.js */; };
1C73613562EB375F53A0BD03 /* ServerDownloadDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736595533B56039C417E0D /* ServerDownloadDelegate.swift */; };
1C7361B3AF46CEB30D3F4FA0 /* KSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736AE5021E3D985FE3402D /* KSettings.swift */; };
1C73631EACF56BABD3B2BCFB /* LayoutTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */; };
@ -28,10 +29,12 @@
1C736690D123BD4B24874394 /* pathfinder.scpt in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369BED02028D8564E82D5 /* pathfinder.scpt */; };
1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */; };
1C7366FE5C760C8D5117207F /* SVideoLoopPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360F9835128FC0A198ED0 /* SVideoLoopPlayer.swift */; };
1C7366FF0651A802F09936D6 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C17C40DAE162AF8DDE3 /* WebViewModel.swift */; };
1C7367084839D2E8B180DB74 /* UIViewController+Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360295486647982CFEACF /* UIViewController+Alert.swift */; };
1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */; };
1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736253AB7A95EA41B605B7 /* ItemModel.swift */; };
1C736771C503FB0D52AEB8F7 /* kplayer.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C73625012D50E457D18A785 /* kplayer.js */; };
1C736776CF759CA3DB136F33 /* KBrowserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EF64DE56AD058A4F612 /* KBrowserView.swift */; };
1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; };
1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */; };
1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364709899FF62774B0199 /* VideoHelper.swift */; };
@ -42,6 +45,7 @@
1C73696E4C0353053BF98031 /* links.html in Resources */ = {isa = PBXBuildFile; fileRef = 1C73615FFA2AA98BD1C56CD4 /* links.html */; };
1C736998044A9A7D89411892 /* AsyncImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360B6D0757D4FB6433E7B /* AsyncImage.swift */; };
1C7369ABC44CFB530EA71FB6 /* HeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */; };
1C7369C0A66A8F8CB0E54460 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D27EC608FAAFDB4A68C /* WebView.swift */; };
1C736A06A2AD75B8C14EEBBE /* HtmlParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */; };
1C736A5FA5BA53B2597F2ED7 /* Kirschkeks-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */; };
1C736A622876405F3EE2D043 /* EditItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7366C09381DC0052B52B69 /* EditItemView.swift */; };
@ -107,6 +111,7 @@
1C736260E748CF136FF37EA7 /* UploadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = "<group>"; };
1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KPersistentContainer.swift; sourceTree = "<group>"; };
1C73631C96E6C860833052CA /* ItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = "<group>"; };
1C7363A2B3338DAF752D8CE9 /* download.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = download.js; sourceTree = "<group>"; };
1C73645DBD6499A726D34973 /* links.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = links.html; sourceTree = "<group>"; };
1C73647019E6C2E822127BA3 /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
1C7364709899FF62774B0199 /* VideoHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoHelper.swift; sourceTree = "<group>"; };
@ -134,7 +139,9 @@
1C736B41C6AC33F3FA592C63 /* MediaModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaModel.swift; sourceTree = "<group>"; };
1C736B794396F2E50387B8F2 /* stringutil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = stringutil.swift; sourceTree = "<group>"; };
1C736BC4450890C45F8FBC63 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = "<group>"; };
1C736C17C40DAE162AF8DDE3 /* WebViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewModel.swift; sourceTree = "<group>"; };
1C736C94157754DE1C808173 /* KSettingsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettingsModel.swift; sourceTree = "<group>"; };
1C736D27EC608FAAFDB4A68C /* WebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = "<group>"; };
1C736D50A22FC4553165199D /* FlexibleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlexibleView.swift; sourceTree = "<group>"; };
1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = "<group>"; };
1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParser.swift; sourceTree = "<group>"; };
@ -142,6 +149,7 @@
1C736DCD945ABAE984FF43EF /* KNetworkProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KNetworkProtocol.swift; sourceTree = "<group>"; };
1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailCache.swift; sourceTree = "<group>"; };
1C736EEC570C71AAC50F2E95 /* SVideoModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVideoModel.swift; sourceTree = "<group>"; };
1C736EF64DE56AD058A4F612 /* KBrowserView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KBrowserView.swift; sourceTree = "<group>"; };
1C736F9338CE36708244D42A /* DataLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataLoadOperation.swift; sourceTree = "<group>"; };
6D522F61736592330F481B4F /* Pods-kplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-kplayer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-kplayer/Pods-kplayer.debug.xcconfig"; sourceTree = "<group>"; };
8B75159FFCD5A882E6F167FE /* Pods_kplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_kplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -203,6 +211,7 @@
1C73625012D50E457D18A785 /* kplayer.js */,
1C73615FFA2AA98BD1C56CD4 /* links.html */,
1C73645DBD6499A726D34973 /* links.html */,
1C7363A2B3338DAF752D8CE9 /* download.js */,
);
path = server;
sourceTree = "<group>";
@ -289,6 +298,16 @@
path = video;
sourceTree = "<group>";
};
1C736FEAC391DC13A5D6E61E /* web */ = {
isa = PBXGroup;
children = (
1C736C17C40DAE162AF8DDE3 /* WebViewModel.swift */,
1C736EF64DE56AD058A4F612 /* KBrowserView.swift */,
1C736D27EC608FAAFDB4A68C /* WebView.swift */,
);
path = web;
sourceTree = "<group>";
};
8052F5B3AAC2535E5C08A529 /* Pods */ = {
isa = PBXGroup;
children = (
@ -347,6 +366,7 @@
1C7363B608460DED4F522D1C /* photo */,
1C736F3946A38499113D351A /* video */,
1C73633B55AC5378053BDCE2 /* server */,
1C736FEAC391DC13A5D6E61E /* web */,
);
path = kplayer;
sourceTree = "<group>";
@ -598,6 +618,10 @@
1C736690D123BD4B24874394 /* pathfinder.scpt in Sources */,
1C736559059B6FBA1C661191 /* KToggleButton.swift in Sources */,
1C7366FE5C760C8D5117207F /* SVideoLoopPlayer.swift in Sources */,
1C7366FF0651A802F09936D6 /* WebViewModel.swift in Sources */,
1C736776CF759CA3DB136F33 /* KBrowserView.swift in Sources */,
1C7369C0A66A8F8CB0E54460 /* WebView.swift in Sources */,
1C736110BE0903CEFE5C18A4 /* download.js in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

2
kplayer/core/MediaItem.swift

@ -91,6 +91,8 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject, Identifiable {
var tags = [String]()
var cookies = ""
convenience init(model: MediaModel) {
self.init(name: model.name, path: model.path, root: model.root, type: model.type)

64
kplayer/detail/BrowserController.swift

@ -32,6 +32,7 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate,
var currentItem : MediaItem?
var web : WebBrowserViewController?
let config = WKWebViewConfiguration()
var dl = false
@ -58,7 +59,6 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate,
let preferences = WKPreferences()
preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
let config = WKWebViewConfiguration()
config.preferences = preferences
config.allowsInlineMediaPlayback = true
config.allowsAirPlayForMediaPlayback = false
@ -214,7 +214,8 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate,
}
let name = url2.lastPathComponent
let hostcomp = currentItem!.name.split(separator: ".")
let host = currentItem!.name
let hostcomp = host.split(separator: ".")
let site = String(hostcomp[hostcomp.count-2])
if ((url2.pathExtension == "mp4" || url2.pathExtension == "m3u8") && dl) {
@ -230,8 +231,61 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate,
}
let item = MediaItem(name: name, path: name, root: site, type: ItemType.VIDEO)
item.externalURL = url
showVideo(selectedItem: item)
if url.starts(with: "/") {
item.externalURL = host + url
}
else {
item.externalURL = url
}
if hostcomp.count > 2 {
let hostEnd = String(hostcomp[1] + "." + hostcomp[2])
getCookieHeader(for: hostEnd) { dictionary in
print(dictionary)
item.cookies = dictionary
self.showVideo(selectedItem: item)
}
}
else {
showVideo(selectedItem: item)
}
}
func getCookies(for domain: String? = nil, completion: @escaping ([String : Any])->()) {
let httpCookieStore = config.websiteDataStore.httpCookieStore
var cookieDict = [String : AnyObject]()
httpCookieStore.getAllCookies { cookies in
for cookie in cookies {
if let domain = domain {
if cookie.domain.contains(domain) {
cookieDict[cookie.name] = cookie.properties as AnyObject?
}
} else {
cookieDict[cookie.name] = cookie.properties as AnyObject?
}
}
completion(cookieDict)
}
}
func getCookieHeader(for domain: String? = nil, completion: @escaping (String)->()) {
let httpCookieStore = config.websiteDataStore.httpCookieStore
var cookieDict = [HTTPCookie]()
httpCookieStore.getAllCookies { cookies in
for cookie in cookies {
if let domain = domain {
if cookie.domain.contains(domain) {
cookieDict.append(cookie)
}
}
}
let values = HTTPCookie.requestHeaderFields(with: cookieDict)
completion(values["Cookie"]!)
}
}
@ -266,7 +320,7 @@ class BrowserController : UIViewController, ItemController, WebBrowserDelegate,
getWindow().rootViewController!.definesPresentationContext = true
pc.modalPresentationStyle = .overCurrentContext
getWindow().rootViewController!.present(pc, animated: true)
present(pc, animated: true)
}
func getWindow() -> UIWindow {

22
kplayer/detail/DetailViewController+Show.swift

@ -35,6 +35,28 @@ extension DetailViewController {
}
func showWeb(selectedItem: MediaItem) {
if selectedItem.name.contains("strip") {
let chromeURL = selectedItem.name.replacingOccurrences(of: "https://", with: "googlechrome://")
UIApplication.shared.open(URL(string: chromeURL)!)
return
}
var webView = KBrowserView(item: selectedItem.name)
webView.completionHandler = {
self.dismiss(animated: true, completion: nil);
}
let pc = UIHostingController(rootView: webView)
pc.view.backgroundColor = .black
getWindow().rootViewController!.definesPresentationContext = true
pc.modalPresentationStyle = .overCurrentContext
getWindow().rootViewController!.present(pc, animated: true)
}
func showWeb2(selectedItem: MediaItem) {
if selectedItem.name.contains("strip") {
let chromeURL = selectedItem.name.replacingOccurrences(of: "https://", with: "googlechrome://")

4
kplayer/video/SVideoModel.swift

@ -47,6 +47,8 @@ class SVideoModel : ObservableObject {
}
func currentPlayerItem() -> AVPlayerItem {
AVPlayerItem(asset: AVAsset(url: currentSnapshot.playerURL!))
let values = currentSnapshot.cookies
let cookieOptions = ["AVURLAssetHTTPHeaderFieldsKey": ["Cookie": values]]
return AVPlayerItem(asset: AVURLAsset(url: currentSnapshot.playerURL!, options: cookieOptions))
}
}

266
kplayer/web/KBrowserView.swift

@ -0,0 +1,266 @@
//
// KBrowserView.swift
// SwiftUIWebView
//
// Created by Md. Yamin on 4/24/20.
// Copyright © 2020 Md. Yamin. All rights reserved.
//
import SwiftUI
struct KBrowserView: View {
@ObservedObject var viewModel = WebViewModel()
@State var downloadOptions = false
@State var showLoader = false
@State var showVideoView = false
@State var message = ""
@State var webTitle = ""
@State var dlUrls = [String]()
var item: String
var completionHandler: (() -> Void)?
@State var videoModel: SVideoModel?
// For WebView's forward and backward navigation
var webViewNavigationBar: some View {
VStack(spacing: 0) {
Divider()
HStack {
Spacer()
Button(action: {
self.viewModel.webViewNavigationPublisher.send(.backward)
}) {
Image(systemName: "chevron.left")
.font(.system(size: 20, weight: .regular))
.imageScale(.large)
.foregroundColor(.gray)
}
Group {
Spacer()
Divider()
Spacer()
}
Button(action: {
self.viewModel.webViewNavigationPublisher.send(.forward)
}) {
Image(systemName: "chevron.right")
.font(.system(size: 20, weight: .regular))
.imageScale(.large)
.foregroundColor(.gray)
}
Group {
Spacer()
Divider()
Spacer()
}
Button(action: {
self.viewModel.webViewNavigationPublisher.send(.reload)
}) {
Image(systemName: "arrow.clockwise")
.font(.system(size: 20, weight: .regular))
.imageScale(.large)
.foregroundColor(.gray).padding(.bottom, 4)
}
Spacer()
}.frame(height: 45)
Divider()
}
}
var body: some View {
ZStack {
VStack(spacing: 0) {
HStack {
Button(action: {
completionHandler!()
}, label: {
Text("cancel")
})
.buttonStyle(BorderlessButtonStyle())
Spacer()
Button(action: {
self.viewModel.valuePublisher.send("")
}, label: {
Text("download")
})
.buttonStyle(BorderlessButtonStyle()).confirmationDialog("Open", isPresented: $downloadOptions) {
let dlcount = dlUrls.count
ForEach(0..<dlcount) { index in
let name = makeLabel(url: dlUrls[index])
Button(name) {
self.preview(url: dlUrls[index])
}
}
Button("cancel", role: .cancel) {
downloadOptions = false
}
}.onReceive(self.viewModel.downloadPublisher.receive(on: RunLoop.main)) { value in
print(value)
downloadOptions = true
dlUrls = value
}
}
/* Here I created a text field that takes string value and when send
button is clicked 'viewModel.valuePublisher' sends that value to WebView
then WebView sends that value to web app that you will load. In this
project's local .html file can not receive it because it is static you should
test with a web app then it will work because static website can not receive values
at runtime where dynamic web app can */
Text(webTitle).font(.title).onReceive(self.viewModel.showWebTitle.receive(on: RunLoop.main)) { value in
self.webTitle = value
}
/* This is our WebView. Here if you pass .localUrl it will load LocalWebsite.html file
into the WebView and if you pass .publicUrl it will load the public website depending on
your url provided. See WebView implementation for more info. */
WebView(viewModel: viewModel, url: item).overlay (
RoundedRectangle(cornerRadius: 4, style: .circular)
.stroke(Color.gray, lineWidth: 0.5)
).padding(.leading, 20).padding(.trailing, 20)
webViewNavigationBar
}.onReceive(self.viewModel.showLoader.receive(on: RunLoop.main)) { value in
self.showLoader = value
}
// A simple loader that is shown when WebView is loading any page and hides when loading is finished.
if showLoader {
// Loader()
}
}.fullScreenCover(isPresented: $showVideoView) {
SVideoPlayer(completionHandler: { saved in
showVideoView = false
}, model: videoModel!)
}
}
func makeLabel(url s: String) -> String {
var name = s
if let u = URL(string: s) {
name = u.lastPathComponent
if s.contains("720") {
name = name + "720 "
}
if s.contains("1080") {
name = name + "1080 "
}
if s.contains("480") {
name = name + "480 "
}
}
return name
}
func preview(url: String) {
let url2 = URL(string: url)!
if (url2.pathExtension == "zip") {
downloadZip(url2, path: "download")
return
}
if (url2.pathExtension == "jpg") {
downloadZip(url2, path: "images/new")
return
}
let name = url2.lastPathComponent
let host = item
let hostcomp = host.split(separator: ".")
let site = String(hostcomp[hostcomp.count-2])
// if ((url2.pathExtension == "mp4" || url2.pathExtension == "m3u8") && dl) {
// NetworkManager.sharedInstance.downloadToServer(path: site, url: url2, result: {
// (r) in
// print(r)
// self.showAlert(title: "download ready", message: r)
// if (r == "exists") {
//
// }
// })
// return
// }
let item = MediaItem(name: name, path: name, root: site, type: ItemType.VIDEO)
if url.starts(with: "/") {
item.externalURL = host + url
}
else {
item.externalURL = url
}
if hostcomp.count > 2 {
let hostEnd = String(hostcomp[1] + "." + hostcomp[2])
getCookieHeader(for: hostEnd) { dictionary in
print(dictionary)
item.cookies = dictionary
self.showVideo(selectedItem: item)
}
}
else {
showVideo(selectedItem: item)
}
}
func getCookieHeader(for domain: String? = nil, completion: @escaping (String)->()) {
let httpCookieStore = viewModel.httpCookieStore!
var cookieDict = [HTTPCookie]()
httpCookieStore.getAllCookies { cookies in
for cookie in cookies {
if let domain = domain {
if cookie.domain.contains(domain) {
cookieDict.append(cookie)
}
}
}
let values = HTTPCookie.requestHeaderFields(with: cookieDict)
completion(values["Cookie"]!)
}
}
func showVideo(selectedItem: MediaItem) {
var se = selectedItem
var children = [MediaItem]()
var clonedChildren = [MediaItem]()
var baseItem = selectedItem
if baseItem.type == ItemType.SNAPSHOT {
baseItem = selectedItem.parent!
}
children = baseItem.children
clonedChildren = baseItem.clone().children
videoModel = SVideoModel(allItems: children, currentSnapshot: se, baseItem: baseItem)
videoModel!.edit = LocalManager.sharedInstance.settings.edit
videoModel!.loop = LocalManager.sharedInstance.settings.autoloop
videoModel!.zoomed = LocalManager.sharedInstance.settings.zoomed
showVideoView = true
}
private func downloadZip(_ url: URL, path: String) {
NetworkManager.sharedInstance.download(url: url, path: path) { url in
// self.showAlert(title: url.lastPathComponent, message: "ready")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
KBrowserView(item: "www.google.com")
}
}

189
kplayer/web/WebView.swift

@ -0,0 +1,189 @@
//
// WebView.swift
// SwiftUIWebView
//
// Created by Md. Yamin on 4/25/20.
// Copyright © 2020 Md. Yamin. All rights reserved.
//
import Foundation
import UIKit
import SwiftUI
import Combine
import WebKit
// MARK: - WebViewHandlerDelegate
// For printing values received from web app
protocol WebViewHandlerDelegate {
func receivedJsonValueFromWebView(value: [String: Any?])
func receivedStringValueFromWebView(value: String)
}
// MARK: - WebView
struct WebView: UIViewRepresentable, WebViewHandlerDelegate {
func receivedJsonValueFromWebView(value: [String : Any?]) {
print("JSON value received from web is: \(value)")
}
func receivedStringValueFromWebView(value: String) {
print("String value received from web is: \(value)")
}
// Viewmodel object
@ObservedObject var viewModel: WebViewModel
@State var url: String
// Make a coordinator to co-ordinate with WKWebView's default delegate functions
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
// Enable javascript in WKWebView
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
// Here "iOSNative" is our delegate name that we pushed to the website that is being loaded
let coordinator = self.makeCoordinator()
configuration.userContentController.add(coordinator, name: "jsError")
configuration.userContentController.add(coordinator, name: "openDocument")
configuration.preferences = preferences
let webView = WKWebView(frame: CGRect.zero, configuration: configuration)
webView.navigationDelegate = context.coordinator
webView.allowsBackForwardNavigationGestures = true
webView.scrollView.isScrollEnabled = true
viewModel.httpCookieStore = configuration.websiteDataStore.httpCookieStore
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
webView.load(URLRequest(url: URL(string: url)!))
}
class Coordinator : NSObject, WKNavigationDelegate, WKScriptMessageHandler {
var parent: WebView
var delegate: WebViewHandlerDelegate?
var valueSubscriber: AnyCancellable? = nil
var webViewNavigationSubscriber: AnyCancellable? = nil
init(_ uiWebView: WebView) {
self.parent = uiWebView
self.delegate = parent
}
deinit {
valueSubscriber?.cancel()
webViewNavigationSubscriber?.cancel()
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Get the title of loaded webcontent
webView.evaluateJavaScript("document.title") { (response, error) in
if let error = error {
print("Error getting title")
print(error.localizedDescription)
}
guard let title = response as? String else {
return
}
self.parent.viewModel.showWebTitle.send(title)
}
/* An observer that observes 'viewModel.valuePublisher' to get value from TextField and
pass that value to web app by calling JavaScript function */
valueSubscriber = parent.viewModel.valuePublisher.receive(on: RunLoop.main).sink(receiveValue: { value in
do {
let url = NetworkManager.sharedInstance.getDownloadJs()
let js = try String(contentsOf: url, encoding: .utf8)
//let j = js.replacingOccurrences(of: "(absoluteUrl)", with: absoluteUrl)
webView.evaluateJavaScript(js) { (result, err) in
if (err != nil) {
debugPrint("JS ERR: \(String(describing: err))")
}
}
} catch {
print(error)
}
})
// Page loaded so no need to show loader anymore
self.parent.viewModel.showLoader.send(false)
}
/* Here I implemented most of the WKWebView's delegate functions so that you can know them and
can use them in different necessary purposes */
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
// Hides loader
parent.viewModel.showLoader.send(false)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
// Hides loader
parent.viewModel.showLoader.send(false)
}
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
// Shows loader
parent.viewModel.showLoader.send(true)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
// Shows loader
parent.viewModel.showLoader.send(true)
self.webViewNavigationSubscriber = self.parent.viewModel.webViewNavigationPublisher.receive(on: RunLoop.main).sink(receiveValue: { navigation in
switch navigation {
case .backward:
if webView.canGoBack {
webView.goBack()
}
case .forward:
if webView.canGoForward {
webView.goForward()
}
case .reload:
webView.reload()
}
})
}
// This function is essential for intercepting every navigation in the webview
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// Suppose you don't want your user to go a restricted site
// Here you can get many information about new url from 'navigationAction.request.description'
if let host = navigationAction.request.url?.host {
if host == "restricted.com" {
// This cancels the navigation
decisionHandler(.cancel)
return
}
}
// This allows the navigation
decisionHandler(.allow)
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// Make sure that your passed delegate is called
print(message)
if message.name == "jsError" {
if let body = message.body as? String {
print(body)
}
}
if message.name == "openDocument" {
do {
let data = (message.body as! String).data(using: .utf8)
let jsonObject = try JSONSerialization.jsonObject(with: data!, options: [])
parent.viewModel.downloadPublisher.send(jsonObject as! [String])
} catch {
}
}
}
}
}

26
kplayer/web/WebViewModel.swift

@ -0,0 +1,26 @@
//
// File.swift
// SwiftUIWebView
//
// Created by Md. Yamin on 4/25/20.
// Copyright © 2020 Md. Yamin. All rights reserved.
//
import Foundation
import Combine
import WebKit
class WebViewModel: ObservableObject {
var webViewNavigationPublisher = PassthroughSubject<WebViewNavigation, Never>()
var showWebTitle = PassthroughSubject<String, Never>()
var showLoader = PassthroughSubject<Bool, Never>()
var valuePublisher = PassthroughSubject<String, Never>()
var downloadPublisher = PassthroughSubject<[String], Never>()
var httpCookieStore: WKHTTPCookieStore?
}
// For identifiying WebView's forward and backward navigation
enum WebViewNavigation {
case backward, forward, reload
}
Loading…
Cancel
Save