You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
WatchOSPlayer/MusicPlayer Watch App/ContentView.swift

204 lines
7.6 KiB

2 years ago
//
// ContentView.swift
// MusicPlayer Watch App
//
// Created by BillSun on 3/18/23.
//
import SwiftUI
import Network
import AVFoundation
import UIKit
class IDStr : Identifiable {
var s : String = ""
var art : Image?
var m : AVPlayerItem?
init () {self.m = nil}
init (str : String, music : AVPlayerItem) {
self.s = str
self.m = music
}
}
class ListViewModel: ObservableObject {
@Published var music = Array<IDStr>()
func addItem(i : String, m : AVPlayerItem) {
music.append(IDStr(str: i, music: m))
}
func addItem(str : IDStr) {
music.append(str)
}
}
2 years ago
2 years ago
class PlaybackViewProxy {
var pbv : PlaybackView
init(v : PlaybackView) {
self.pbv = v
}
}
2 years ago
struct ContentView: View {
@ObservedObject var music = ListViewModel()
2 years ago
@State var pushState = false
@State var geo :CGSize = .zero
@State var _curr_sel_music : IDStr = IDStr()
2 years ago
let pbv : PlaybackViewProxy
2 years ago
var body: some View {
2 years ago
GeometryReader { geometry in
NavigationView(){
List() {
ForEach(music.music) { m in
VStack(){
Button(m.s, action: {
let idx = music.music.firstIndex { s in
s.s == m.s
}
2 years ago
if (idx != nil) {
if (idx != 0) {
music.music = Array(music.music[idx! ... (music.music.endIndex - 1)] + music.music[0...idx! - 1])
self.player?.removeAllItems()
for i in music.music {
i.m!.seek(to: .zero)
player?.insert(i.m!, after: nil)
}
}
else {
m.m!.seek(to: .zero)
}
}
2 years ago
self._curr_sel_music = m
self.pbv.pbv.update(music: _curr_sel_music)
2 years ago
pushState = true
}).ignoresSafeArea(.all).cornerRadius(.zero).padding(.zero).frame(maxHeight: CGFloat(50)).foregroundColor(.white)
2 years ago
NavigationLink(destination: self.pbv.pbv, isActive: $pushState) {
2 years ago
EmptyView()
}
}
}
2 years ago
Label("\(music.music.count) Files. ", systemImage: "heart.fill").background(.clear).labelStyle(.titleAndIcon).frame(width: geometry.size.width, alignment: .center)
2 years ago
}
}.onAppear {
geo = geometry.size
2 years ago
self.pbv.pbv.parent = self
}
2 years ago
}
}
var player : AVQueuePlayer? = nil
init() {
2 years ago
self.pbv = PlaybackViewProxy(v: PlaybackView())
let base = "https://billsun.dev/webdav/music-test"
let url = URL(string: base)
let request: URLRequest = URLRequest(url: url!)
let session = URLSession(configuration: .default)
let dir = NSHomeDirectory()
session.dataTask(with: request, completionHandler:
{ (data, response, error) -> Void in
2 years ago
if (error != nil) { return }
let reply = String(data: data!, encoding: String.Encoding.utf8)!
do {
2 years ago
let pattern = try Regex(#".*(<a\s+href=\"(.*.(m4a|mp3|wav))\">)"#)
let matched = reply.matches(of: pattern)
var s = Set<String>()
for match in matched {
s.insert(String(match.output[2].substring!))
}
2 years ago
for _file in s {
var file = _file
if _file.count > 68 {
file = _file.removingPercentEncoding ?? _file
if file.count > 36 {
file = String(file.prefix(31) + file.suffix(5))
}
}
let filepath = dir + "/Documents/" + file
var download = true
2 years ago
let check_file = { fpath -> Void in
if(FileManager.default.fileExists(atPath: fpath)) {
let sz = try! FileManager.default.attributesOfItem(atPath: fpath)[FileAttributeKey.size] as! UInt64
download = sz < 40960 // (ignore files <40k)
}
}
2 years ago
check_file(filepath)
check_file("\(dir)/Documents/\(_file)")
if (download) {
session.dataTask(with: URLRequest(url: URL(string: base + "/" + _file)!)) {
(data, response, error) -> Void in
if (error == nil) {
let fp = fopen(filepath, "wb")
data!.withUnsafeBytes({ ptr in
fwrite(ptr, 1, data!.count, fp)
})
fclose(fp)
}
}.resume()
}
}
}catch{}
2 years ago
}
).resume()
let enumerator = FileManager.default.enumerator(atPath: dir + "/Documents/")
enumerator!.forEach({ e in
if (self.player == nil) {
do {
let session = AVAudioSession.sharedInstance()
try session.setCategory(AVAudioSession.Category.playback)
} catch {
print(error)
}
self.player = AVQueuePlayer()
}
let filename = (e as! String)
let file_url = URL(filePath: dir + "/Documents/" + filename)
let asset = AVAsset(url: file_url)
let idstr = IDStr()
2 years ago
let geo = self.geo
asset.loadMetadata(for: .iTunesMetadata) {
items, b in
2 years ago
if (items == nil) { return }
for i in items! {
if(i.identifier == .iTunesMetadataCoverArt) {
Task{
let imageData = try await i.load(.dataValue)
idstr.art = Image(uiImage: UIImage(data: imageData!)!)
2 years ago
/*if (idstr.art != nil) {
2 years ago
idstr.art!.resizable().scaledToFill().frame(width: geo.width, height: geo.height)
2 years ago
}*/
}
}
}
}
let item = AVPlayerItem(url: file_url)
idstr.s = filename.prefix(filename.count - 4).removingPercentEncoding!
idstr.m = item
self.music.addItem(str: idstr)
//item.addObserver(self, forKeyPath: "status", context: nil)
self.player?.insert(item, after: nil)
if (self.player?.status == .failed) {
print(self.player!.error!)
}
else {
self.player?.play()
}
})
2 years ago
}
2 years ago
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().ignoresSafeArea(.all).cornerRadius(.zero).padding(.zero)
2 years ago
}
2 years ago
}