initial commit, minial working code
This commit is contained in:
@@ -6,21 +6,159 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
|
@ObservedObject var music = ListViewModel()
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
|
||||||
Image(systemName: "globe")
|
List() {
|
||||||
.imageScale(.large)
|
ForEach(music.music) { m in
|
||||||
.foregroundColor(.accentColor)
|
|
||||||
Text("Hello, world!")
|
Button(m.s, action: {
|
||||||
|
let idx = music.music.firstIndex { s in
|
||||||
|
s.s == m.s
|
||||||
}
|
}
|
||||||
.padding()
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}).ignoresSafeArea(.all).cornerRadius(.zero).padding(.zero).frame(maxHeight: CGFloat(50)).foregroundColor(.white)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var player : AVQueuePlayer? = nil
|
||||||
|
|
||||||
|
init() {
|
||||||
|
print("'sibal'");
|
||||||
|
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
|
||||||
|
if (error == nil) { return }
|
||||||
|
let reply = String(data: data!, encoding: String.Encoding.utf8)!
|
||||||
|
|
||||||
|
do {
|
||||||
|
let pattern = try Regex(#".*(<a\s+href=\"(.*.m4a)\">)"#)
|
||||||
|
let matched = reply.matches(of: pattern)
|
||||||
|
|
||||||
|
var s = Set<String>()
|
||||||
|
for match in matched {
|
||||||
|
s.insert(String(match.output[2].substring!))
|
||||||
|
}
|
||||||
|
for file in s {
|
||||||
|
let filepath = dir + "/Documents/" + file
|
||||||
|
var download = true
|
||||||
|
if(FileManager.default.fileExists(atPath: filepath)) {
|
||||||
|
let sz = try! FileManager.default.attributesOfItem(atPath: filepath)[FileAttributeKey.size] as! UInt64
|
||||||
|
download = sz < 1024
|
||||||
|
}
|
||||||
|
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{}
|
||||||
|
}
|
||||||
|
).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()
|
||||||
|
asset.loadMetadata(for: .iTunesMetadata) {
|
||||||
|
items, b in
|
||||||
|
for i in items! {
|
||||||
|
if(i.identifier == .iTunesMetadataCoverArt) {
|
||||||
|
Task{
|
||||||
|
let imageData = try await i.load(.dataValue)
|
||||||
|
idstr.art = Image(uiImage: UIImage(data: imageData!)!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
struct ContentView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ContentView()
|
ContentView().ignoresSafeArea(.all).cornerRadius(.zero).padding(.zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,16 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import WatchKit
|
||||||
|
import UserNotifications
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct MusicPlayer_Watch_AppApp: App {
|
struct MusicPlayer_Watch_AppApp: App {
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView()
|
ContentView().cornerRadius(.zero).padding(.zero)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// PlaybackView.swift
|
||||||
|
// MusicPlayer Watch App
|
||||||
|
//
|
||||||
|
// Created by BillSun on 3/18/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PlaybackView: View {
|
||||||
|
var body: some View {
|
||||||
|
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||||
|
}
|
||||||
|
init() {}
|
||||||
|
init(parent:ContentView) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PlaybackView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PlaybackView()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
- push to new view
|
||||||
|
- view with media control and background
|
||||||
|
- control menu
|
||||||
|
- with option to delete song
|
||||||
|
- remove all songs
|
||||||
|
- change data source
|
||||||
|
- multiple datasources (smb, webdav, webserver)
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>audio</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
A95C118729C531C100737618 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95C118629C531C100737618 /* ContentView.swift */; };
|
A95C118729C531C100737618 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95C118629C531C100737618 /* ContentView.swift */; };
|
||||||
A95C118929C531C300737618 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A95C118829C531C300737618 /* Assets.xcassets */; };
|
A95C118929C531C300737618 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A95C118829C531C300737618 /* Assets.xcassets */; };
|
||||||
A95C118C29C531C300737618 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A95C118B29C531C300737618 /* Preview Assets.xcassets */; };
|
A95C118C29C531C300737618 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A95C118B29C531C300737618 /* Preview Assets.xcassets */; };
|
||||||
|
A982CD2A29C5BA7D00A1DBDE /* PlaybackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A982CD2929C5BA7D00A1DBDE /* PlaybackView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -45,6 +46,9 @@
|
|||||||
A95C118629C531C100737618 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
A95C118629C531C100737618 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
A95C118829C531C300737618 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
A95C118829C531C300737618 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
A95C118B29C531C300737618 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
A95C118B29C531C300737618 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
|
A982CD2829C5907A00A1DBDE /* MusicPlayer-Watch-App-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "MusicPlayer-Watch-App-Info.plist"; sourceTree = SOURCE_ROOT; };
|
||||||
|
A982CD2929C5BA7D00A1DBDE /* PlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackView.swift; sourceTree = "<group>"; };
|
||||||
|
A982CD2B29C5BE8800A1DBDE /* TODO.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TODO.md; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -78,10 +82,13 @@
|
|||||||
A95C118329C531C100737618 /* MusicPlayer Watch App */ = {
|
A95C118329C531C100737618 /* MusicPlayer Watch App */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
A982CD2829C5907A00A1DBDE /* MusicPlayer-Watch-App-Info.plist */,
|
||||||
A95C118429C531C100737618 /* MusicPlayerApp.swift */,
|
A95C118429C531C100737618 /* MusicPlayerApp.swift */,
|
||||||
|
A982CD2929C5BA7D00A1DBDE /* PlaybackView.swift */,
|
||||||
A95C118629C531C100737618 /* ContentView.swift */,
|
A95C118629C531C100737618 /* ContentView.swift */,
|
||||||
A95C118829C531C300737618 /* Assets.xcassets */,
|
A95C118829C531C300737618 /* Assets.xcassets */,
|
||||||
A95C118A29C531C300737618 /* Preview Content */,
|
A95C118A29C531C300737618 /* Preview Content */,
|
||||||
|
A982CD2B29C5BE8800A1DBDE /* TODO.md */,
|
||||||
);
|
);
|
||||||
path = "MusicPlayer Watch App";
|
path = "MusicPlayer Watch App";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -193,6 +200,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
A95C118729C531C100737618 /* ContentView.swift in Sources */,
|
A95C118729C531C100737618 /* ContentView.swift in Sources */,
|
||||||
|
A982CD2A29C5BA7D00A1DBDE /* PlaybackView.swift in Sources */,
|
||||||
A95C118529C531C100737618 /* MusicPlayerApp.swift in Sources */,
|
A95C118529C531C100737618 /* MusicPlayerApp.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -328,6 +336,7 @@
|
|||||||
DEVELOPMENT_TEAM = 7P35HLZ26U;
|
DEVELOPMENT_TEAM = 7P35HLZ26U;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "MusicPlayer-Watch-App-Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = MusicPlayer;
|
INFOPLIST_KEY_CFBundleDisplayName = MusicPlayer;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
INFOPLIST_KEY_WKWatchOnly = YES;
|
INFOPLIST_KEY_WKWatchOnly = YES;
|
||||||
@@ -358,6 +367,7 @@
|
|||||||
DEVELOPMENT_TEAM = 7P35HLZ26U;
|
DEVELOPMENT_TEAM = 7P35HLZ26U;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "MusicPlayer-Watch-App-Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = MusicPlayer;
|
INFOPLIST_KEY_CFBundleDisplayName = MusicPlayer;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
INFOPLIST_KEY_WKWatchOnly = YES;
|
INFOPLIST_KEY_WKWatchOnly = YES;
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<VariablesViewState
|
||||||
|
version = "1.0">
|
||||||
|
<ContextStates>
|
||||||
|
<ContextState
|
||||||
|
contextName = "closure #1 in ContentView.init():ContentView.swift">
|
||||||
|
<PersistentStrings>
|
||||||
|
<PersistentString
|
||||||
|
value = "match.output[0].substring!.cString(using: String.Encoding.utf8)">
|
||||||
|
</PersistentString>
|
||||||
|
</PersistentStrings>
|
||||||
|
</ContextState>
|
||||||
|
</ContextStates>
|
||||||
|
</VariablesViewState>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Bucket
|
||||||
|
uuid = "E40B0B2F-0F7C-4049-8384-96A6D265F1C3"
|
||||||
|
type = "1"
|
||||||
|
version = "2.0">
|
||||||
|
</Bucket>
|
||||||
Reference in New Issue
Block a user