Browse Source

Local Files extern

master
marcoschmickler 4 years ago
parent
commit
6f476c0cb3
  1. 4
      kplayer.xcodeproj/project.pbxproj
  2. 16
      kplayer/core/LocalManager.swift
  3. 14
      kplayer/core/MediaItem.swift
  4. 2
      kplayer/detail/DetailViewController.swift
  5. 55
      kplayer/detail/EditItemView.swift
  6. 165
      kplayer/detail/VideoController.swift
  7. 12
      kplayer/master/NetworkDelegate.swift
  8. 26
      kplayer/video/BMPlayer.swift
  9. 30
      kplayer/video/KVideoPlayer.swift

4
kplayer.xcodeproj/project.pbxproj

@ -46,6 +46,7 @@
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 */; };
1C736A78C1F8F41E2AEEF278 /* KVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736E2CD0C1780F4F5AE0C4 /* KVideoPlayer.swift */; };
1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73631C96E6C860833052CA /* ItemType.swift */; };
1C736CB96577F6A9A7BA03E8 /* BMPlayerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F924BD979294C3EE4A /* BMPlayerItem.swift */; };
1C736CD0E54786D3A2405E51 /* BMPlayerLayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7366D766CDE0C9872E86F5 /* BMPlayerLayerView.swift */; };
@ -137,6 +138,7 @@
1C736DBB6986A8B62963FBB3 /* HtmlParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParser.swift; sourceTree = "<group>"; };
1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = "<group>"; };
1C736DFBD072763248412F74 /* BMPlayerClearityChooseButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerClearityChooseButton.swift; sourceTree = "<group>"; };
1C736E2CD0C1780F4F5AE0C4 /* KVideoPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVideoPlayer.swift; sourceTree = "<group>"; };
1C736E51F1A03E3A1200BDB6 /* BMPlayerControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerControlView.swift; sourceTree = "<group>"; };
1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailCache.swift; sourceTree = "<group>"; };
1C736F9338CE36708244D42A /* DataLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataLoadOperation.swift; sourceTree = "<group>"; };
@ -267,6 +269,7 @@
1C7360AE55EB115762C42EB9 /* BMTimeSlider.swift */,
1C7360D6580FB5D09C2BBCCB /* BMPlayerManager.swift */,
1C73610B997EBA367C806C1B /* BMPlayerCompositionResourceDefinition.swift */,
1C736E2CD0C1780F4F5AE0C4 /* KVideoPlayer.swift */,
);
path = video;
sourceTree = "<group>";
@ -584,6 +587,7 @@
1C736A622876405F3EE2D043 /* EditItemView.swift in Sources */,
1C73613562EB375F53A0BD03 /* ServerDownloadDelegate.swift in Sources */,
1C736EC45EE7DA5F7FCE63DA /* LocalManager.swift in Sources */,
1C736A78C1F8F41E2AEEF278 /* KVideoPlayer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

16
kplayer/core/LocalManager.swift

@ -86,9 +86,12 @@ class LocalManager {
let json = item.toJSON()
do {
print(url)
try json.write(to: url.appendingPathExtension("json"), atomically: true, encoding: .utf8)
let input = try String(contentsOf: url)
var jsonurl = url
if url.pathExtension != "json" {
jsonurl = url.appendingPathExtension("json")
}
try json.write(to: jsonurl, atomically: true, encoding: .utf8)
let input = try String(contentsOf: jsonurl)
print(input)
} catch {
print(json)
@ -181,6 +184,7 @@ class LocalManager {
let item = try JSONDecoder().decode(MediaModel.self, from: jsonData)
let mediaItem = MediaItem(model: item)
mediaItem.externalURL = fileURL.absoluteString
mediaItem.localURL = jsonURL
mediaItem.local = true
for i in mediaItem.children {
i.externalURL = fileURL.absoluteString
@ -253,10 +257,16 @@ class LocalManager {
}
if path!.contains("file://") {
if path!.contains("/Application") {
let p = path!.substringAfter("/Documents/")
let fixed = FileHelper.getDocumentsDirectory().appendingPathComponent(p)
return fixed.absoluteString
}
else {
let p = path!.substringAfter("/Documents")
return externalURL + p
}
}
return path
}

14
kplayer/core/MediaItem.swift

@ -5,6 +5,7 @@
import Foundation
import UIKit
import Combine
/**
Repräsentiert ein Item eines der festgelegten Typen.
@ -38,8 +39,18 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject {
// Nutzinhalt
var image: UIImage?
// let didChange = PassthroughSubject<MediaItem,Never>()
@Published
var time: TimeInterval = 0.0
//
// {
// willSet {
// // print(newValue)
// // didChange.send(self)
// self.objectWillChange.send()
// }
// }
@Published
var length: TimeInterval = 0.0
@ -49,13 +60,14 @@ class MediaItem: CustomDebugStringConvertible, ObservableObject {
var thumbUrl: String?
var externalURL: String?
var localURL: URL?
var local = false
var external = false
var leaf = false
var cancelled = false
var scale = 0.0
var scale = 1.0
var offset = CGPoint()
var size = CGSize()

2
kplayer/detail/DetailViewController.swift

@ -307,7 +307,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
}
}
else if items.local {
self.delegate!.deleteLocal(selectedItem: items)
// self.delegate!.deleteLocal(selectedItem: items)
}
}))

55
kplayer/detail/EditItemView.swift

@ -10,37 +10,66 @@ struct EditItemView : View {
@ObservedObject
var item: MediaItem
var len: Double
var seek: (Double) -> Void
var capture: (MediaItem) -> Void
var body: some View {
Form {
Section(header: Text("K Settings")) {
VStack {
Text("Size")
Slider(value: $item.time, in: 0...1000)
Slider(value: $item.length, in: 0...1000)
Slider(value: Binding<Double>(
get: { item.time },
set: {
item.time = $0
seek(item.time)
}
), in: 0...len)
Text("Start \(item.time, specifier: "%.1f") Length \(item.length, specifier: "%.1f")")
Slider(value: Binding<Double>(
get: { item.length },
set: {
item.length = $0
seek(item.time + item.length)
}
), in: 1...500)
Toggle(isOn: $item.loop, label: {
Text("Loop")
})
}
}
Text("Zoom \(item.scale, specifier: "%.1f") X \(item.offset.x, specifier: "%.1f") Y \(item.offset.y, specifier: "%.1f") ")
HStack {
Button(action: {
capture(item)
item.objectWillChange.send()
}, label: {
Text("ok")
Text("Zoom")
});
Button(action: {
}, label: {
Text("cancel")
})
// Button(action: {
// item.scale = 1.0
// item.offset = CGPoint(x: 0,y: 0)
// item.objectWillChange.send()
// }, label: {
// Text("Reset")
// });
}
}.background(Color.clear)
}.background(Color.clear)
}
.onAppear {
UITableView.appearance().backgroundColor = .clear
}
.onDisappear {
UITableView.appearance().backgroundColor = .systemBackground
}
.frame(height: 350, alignment: .top)
//Spacer()
}
}
struct EditItemView_Previews: PreviewProvider {
static var previews: some View {
EditItemView(item: MediaItem(name: "extern", path:"", root: "", type: ItemType.FAVROOT))
EditItemView(item: MediaItem(name: "extern", path: "", root: "", type: ItemType.FAVROOT), len: 1000, seek: { v in }, capture: { v in })
}
}

165
kplayer/detail/VideoController.swift

@ -36,7 +36,6 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
var downloadDelegate: DownloadDelegate?
var loopStart = 0.0
var loopEnd = 0.0
var completionHandler: (() -> Void)?
@ -56,11 +55,11 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
var aspect = 1
var loopOption = 0
var loopMode = false
var thumbnailTime: TimeInterval = 0.0
var edit = true
var edit = false
var allowEdit = true
var index = 0
@ -74,7 +73,7 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
barbutton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(VideoController.captureThumbnail));
navigationItem.rightBarButtonItems = [barbutton!]
backButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(VideoController.back(_:)))
backButton = UIBarButtonItem(title: "0:00", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.back(_:)))
speedButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.speed(_:)))
loopButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.loop(_:)))
aspectButton = UIBarButtonItem(title:"1", style:UIBarButtonItem.Style.plain, target: self, action: #selector(VideoController.aspect(_:)))
@ -106,16 +105,37 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
update()
}
loopMode = detailDelegate!.settings().autoloop
updateLoop()
}
func editItem() {
let kv = EditItemView(item: currentSnapshot!)
let currentItem = player.playerLayer?.player?.currentItem
let totalTime : Double
if let playerItem = currentItem {
totalTime = TimeInterval(playerItem.duration.value) / TimeInterval(playerItem.duration.timescale)
}
else {
totalTime = 1000
}
let kv = EditItemView(item: currentSnapshot!, len: totalTime, seek:
{ value in
print(value)
self.player.seekSmoothlyToTime(newChaseTime: value)
}, capture: { item in
item.scale = Double(self.player.zoom)
item.offset = CGPoint(x: self.player.xpos, y: self.player.ypos)
})
let pc = UIHostingController(rootView: kv)
pc.view.backgroundColor = .clear
let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack.
// navController.modalPresentationStyle = .fullScreen
// navController.modalPresentationStyle = .popover
present(navController, animated: false, completion: nil)
}
@ -146,6 +166,8 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
}
else {
edit = true
loopMode = false
updateLoop()
reviewButton!.tintColor = UIColor.yellow
}
}
@ -235,7 +257,7 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
file = file.appendingPathExtension("mp4")
}
print (file)
var dur = loopEnd - loopStart
var dur = player.loopEnd - loopStart
if (dur < 0) {
return
}
@ -278,9 +300,7 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
}
else {
if player.isPlayToTheEnd {
player.seek(0, completion: {[weak self] in
self!.player.play()
})
player.seekSmoothlyToTime(newChaseTime: 0)
player.isPlayToTheEnd = false
}
player.play()
@ -289,34 +309,22 @@ print("play")
}
@objc func loop(_ sender: AnyObject) {
loopOption += 1
if loopOption >= 2 {
loopOption = 0
if edit {
loopMode = false
}
if loopOption == 1 {
let t = player.playerLayer!.player!.currentTime()
loopEnd = (Double(t.value) / Double(t.timescale)) - 2.0
else {
loopMode = !loopMode
}
updateLoop()
}
func updateLoop() {
if loopOption == 1 {
var dur = loopEnd - loopStart
if dur > 0 {
loopButton!.title = "(\(dur))"
if loopMode {
loopButton!.title = "loop"
}
else {
loopButton!.title = "cont"
}
}
else {
loopButton!.title = "loop"
}
}
@objc func aspect(_ sender: AnyObject) {
@ -439,41 +447,51 @@ print("finish")
}
@objc func thumbnailClicked(_ source: UIButton) {
currentSnapshot = buttons[source]
player.seek(buttons[source]!.time)
loopStart = buttons[source]!.time
loopOption = 0
loopButton!.title = "loop"
if let currentSnapshot = buttons[source] {
gotoSnapshot(currentSnapshot: currentSnapshot)
if (edit) {
// editItem()
editItem()
}
}
// moviePlayer!.currentPlaybackRate = Float(speedOptions[speedOption])
// print("goto \(buttons[source]!.time!) is \(moviePlayer!.currentPlaybackTime)")
}
private func gotoSnapshot(currentSnapshot: MediaItem) {
player.seekSmoothlyToTime(newChaseTime: currentSnapshot.time)
loopStart = currentSnapshot.time
player.loopEnd = loopStart + currentSnapshot.length
if loopMode && currentSnapshot.scale > 0 {
player.zoom = Float(currentSnapshot.scale)
player.xpos = currentSnapshot.offset.x
player.ypos = currentSnapshot.offset.y
player.transformLayer()
}
self.currentSnapshot = currentSnapshot
}
@objc func update() {
reviewButton!.title = currentItem!.name
if currentItem!.type == ItemType.SNAPSHOT {
player.seek(currentItem!.time)
if detailDelegate!.settings().autoloop && currentItem!.loop {
loopStart = currentItem!.time
loopEnd = currentItem!.time + currentItem!.length
loopOption = 1
}
player.loopEnd = currentItem!.time + currentItem!.length
currentItem = currentItem!.parent
} else {
if !currentItem!.children.isEmpty {
player.seek(currentItem!.children[0].time)
player.seekSmoothlyToTime(newChaseTime: currentItem!.children[0].time)
}
else {
let duration = player.playerLayer!.playerItem!.duration
if !duration.isIndefinite {
print(duration)
player.seek(duration.seconds / 2.0)
player.seekSmoothlyToTime(newChaseTime: duration.seconds / 2.0)
}
}
}
@ -667,6 +685,61 @@ print("finish")
addItemButton(newItem)
}
func moveUp() {
let t = Date().timeIntervalSince1970
if lastMove + 2 > t {
return
}
lastMove = t
if let c = currentItem?.children {
if !c.isEmpty{
if let s = currentSnapshot {
if var i = c.firstIndex { x in x===s } {
print(i)
i+=1
if i >= c.count {
i = 0
}
gotoSnapshot(currentSnapshot: c[i])
}
}
else {
gotoSnapshot(currentSnapshot: c[0])
}
}
}
}
var lastMove = 0.0
func moveDown() {
let t = Date().timeIntervalSince1970
if lastMove + 2 > t {
return
}
lastMove = t
if let c = currentItem?.children {
if !c.isEmpty{
if let s = currentSnapshot {
if var i = c.firstIndex { x in x===s } {
print(i)
i-=1
if i < 0 {
i = c.count-1
}
gotoSnapshot(currentSnapshot: c[i])
}
}
else {
gotoSnapshot(currentSnapshot: c[0])
}
}
}
}
func bmPlayer(player: BMPlayer) {
let speed = Float(speedOptions[speedOption])
if let pl = player.playerLayer!.player {
@ -684,12 +757,16 @@ print("finish")
}
func bmPlayer(player: BMPlayer, playTimeDidChange currentTime: TimeInterval, totalTime: TimeInterval) {
if loopOption == 1 {
if currentTime > loopEnd {
player.seek(loopStart)
if loopMode {
if currentTime > player.loopEnd && loopStart < player.loopEnd {
player.chaseTime = CMTime.zero
player.seekSmoothlyToTime(newChaseTime: loopStart)
}
}
if let b = backButton {
b.title = BMPlayer.formatSecondsToString(currentTime)
}
// print("Time")
}
func bmPlayer(player: BMPlayer, playerIsPlaying playing: Bool) {

12
kplayer/master/NetworkDelegate.swift

@ -81,6 +81,17 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
}
func deleteThumb(selectedItem c: MediaItem) {
if c.local {
if let url = c.parent?.localURL {
do {
try FileManager.default.removeItem(atPath: c.thumbUrl!)
} catch {
print(c.thumbUrl)
}
LocalManager.sharedInstance.saveFavDir(url: url, item: c.parent!)
}
}
else {
let t = c.time
let ms = Int(t * 1000)
let p = c.snapshotDirPathForVideo + "\(ms).jpg"
@ -89,6 +100,7 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
NetworkManager.sharedInstance.deleteThumb(p)
NetworkManager.sharedInstance.deleteThumb(pt)
}
}
func saveItem(selectedItem: MediaItem) {
var item = selectedItem

26
kplayer/video/BMPlayer.swift

@ -18,6 +18,8 @@ public protocol BMPlayerDelegate : class {
func bmPlayer(player: BMPlayer, playTimeDidChange currentTime : TimeInterval, totalTime: TimeInterval)
func bmPlayer(player: BMPlayer, playerIsPlaying playing: Bool)
func bmPlayer(player: BMPlayer, playerOrientChanged isFullscreen: Bool)
func moveUp()
func moveDown()
}
/**
@ -37,6 +39,7 @@ open class BMPlayer: UIView {
open var aspecty = Float(1.0)
var xpos = 0.0
var ypos = 0.0
var loopEnd = 0.0
open var pinchGesture: UIPinchGestureRecognizer!
open var moveGesture: UIPanGestureRecognizer!
@ -74,6 +77,12 @@ open class BMPlayer: UIView {
if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
zoom *= Float(gestureRecognizer.scale)
if zoom < 0.1 {
zoom = 1.0
xpos = 0
ypos = 0
}
gestureRecognizer.scale = 1.0
transformLayer()
@ -259,6 +268,7 @@ open class BMPlayer: UIView {
- parameter to: target time
*/
open func seek(_ to:TimeInterval, completion: (()->Void)? = nil) {
chaseTime = CMTime(value: Int64(to * 10000), timescale: CMTimeScale(10000))
playerLayer?.seek(to: to, completion: completion)
}
@ -306,7 +316,11 @@ open class BMPlayer: UIView {
}
// https://gist.github.com/shaps80/ac16b906938ad256e1f47b52b4809512
private func seekSmoothlyToTime(newChaseTime: CMTime) {
public func seekSmoothlyToTime(newChaseTime: Double) {
seekSmoothlyToTime(newChaseTime: CMTime(value: Int64(newChaseTime * 10000), timescale: CMTimeScale(10000)))
}
public func seekSmoothlyToTime(newChaseTime: CMTime) {
if CMTimeCompare(newChaseTime, chaseTime) != 0 {
chaseTime = newChaseTime
@ -425,6 +439,10 @@ open class BMPlayer: UIView {
print(skipAmount)
if (skipAmount > 1500) {
self.sumTime += TimeInterval(30)
if sumTime > loopEnd {
loopEnd = 1000000;
}
} else if skipAmount > 0 {
self.sumTime += TimeInterval(10)
} else if skipAmount > -1500 {
@ -469,6 +487,12 @@ open class BMPlayer: UIView {
open func verticalMoved(_ value: CGFloat) {
print(value)
if (value < -100) {
delegate?.moveDown()
// controlView(controlView: controlView, didChooseDefinition: currentDefinition+1);
}
if (value > 100) {
delegate?.moveUp()
// controlView(controlView: controlView, didChooseDefinition: currentDefinition+1);
}

30
kplayer/video/KVideoPlayer.swift

@ -0,0 +1,30 @@
//
// Created by Marco Schmickler on 14.11.21.
// Copyright (c) 2021 Marco Schmickler. All rights reserved.
//
import SwiftUI
import AVKit
struct KVideoPlayer: View {
private let player = AVPlayer(url: URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")!)
var body: some View {
VideoPlayer(player: player)
.onAppear() {
// Start the player going, otherwise controls don't appear
player.play()
}
.onDisappear() {
// Stop the player when the view disappears
player.pause()
}
}
}
struct KVideoPlayer_Previews: PreviewProvider {
static var previews: some View {
KVideoPlayer()
}
}
Loading…
Cancel
Save