diff --git a/MusicPlayer Watch App/ContentView.swift b/MusicPlayer Watch App/ContentView.swift index 5eb8c6f..87a014f 100644 --- a/MusicPlayer Watch App/ContentView.swift +++ b/MusicPlayer Watch App/ContentView.swift @@ -33,11 +33,19 @@ class ListViewModel: ObservableObject { } } +class PlaybackViewProxy { + var pbv : PlaybackView + init(v : PlaybackView) { + self.pbv = v + } +} + struct ContentView: View { @ObservedObject var music = ListViewModel() @State var pushState = false @State var geo :CGSize = .zero @State var _curr_sel_music : IDStr = IDStr() + let pbv : PlaybackViewProxy var body: some View { GeometryReader { geometry in NavigationView(){ @@ -61,18 +69,21 @@ struct ContentView: View { m.m!.seek(to: .zero) } } + self._curr_sel_music = m + self.pbv.pbv.update(music: _curr_sel_music) pushState = true - _curr_sel_music = m }).ignoresSafeArea(.all).cornerRadius(.zero).padding(.zero).frame(maxHeight: CGFloat(50)).foregroundColor(.white) - NavigationLink(destination: PlaybackView(parent:self, music: _curr_sel_music), isActive: $pushState) { + NavigationLink(destination: self.pbv.pbv, isActive: $pushState) { EmptyView() } } } + Label("\(music.music.count) Files. ", systemImage: "heart.fill").background(.clear).labelStyle(.titleAndIcon).frame(width: geometry.size.width, alignment: .center) } }.onAppear { geo = geometry.size + self.pbv.pbv.parent = self } } } @@ -80,7 +91,7 @@ struct ContentView: View { var player : AVQueuePlayer? = nil init() { - print("'sibal'"); + self.pbv = PlaybackViewProxy(v: PlaybackView()) let base = "https://billsun.dev/webdav/music-test" let url = URL(string: base) let request: URLRequest = URLRequest(url: url!) @@ -89,27 +100,38 @@ struct ContentView: View { session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in - if (error == nil) { return } + if (error != nil) { return } let reply = String(data: data!, encoding: String.Encoding.utf8)! do { - let pattern = try Regex(#".*()"#) + let pattern = try Regex(#".*()"#) let matched = reply.matches(of: pattern) var s = Set() for match in matched { s.insert(String(match.output[2].substring!)) } - for file in s { + 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 - if(FileManager.default.fileExists(atPath: filepath)) { - let sz = try! FileManager.default.attributesOfItem(atPath: filepath)[FileAttributeKey.size] as! UInt64 - download = sz < 1024 + 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) + } } - if (download) - { - session.dataTask(with: URLRequest(url: URL(string: base + "/" + file)!)) { + + 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") @@ -120,7 +142,6 @@ struct ContentView: View { } }.resume() } - } }catch{} } @@ -143,14 +164,15 @@ struct ContentView: View { let geo = self.geo asset.loadMetadata(for: .iTunesMetadata) { items, b in + 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!)!) - if (idstr.art != nil) { + /*if (idstr.art != nil) { idstr.art!.resizable().scaledToFill().frame(width: geo.width, height: geo.height) - } + }*/ } } } diff --git a/MusicPlayer Watch App/MusicPlayerApp.swift b/MusicPlayer Watch App/MusicPlayerApp.swift index 9694bb7..f1d0dca 100644 --- a/MusicPlayer Watch App/MusicPlayerApp.swift +++ b/MusicPlayer Watch App/MusicPlayerApp.swift @@ -7,7 +7,6 @@ import SwiftUI import WatchKit -import UserNotifications @main struct MusicPlayer_Watch_AppApp: App { diff --git a/MusicPlayer Watch App/PlaybackView.swift b/MusicPlayer Watch App/PlaybackView.swift index 268ed86..293e289 100644 --- a/MusicPlayer Watch App/PlaybackView.swift +++ b/MusicPlayer Watch App/PlaybackView.swift @@ -6,23 +6,113 @@ // import SwiftUI +import UIKit + + struct PlaybackView: View { var placeholder: Image? = nil - - var body: some View { - placeholder == nil ? - nil : placeholder!.resizable().scaledToFill() - + var music : IDStr? = nil + var parent : ContentView? = nil + //@ObservedObject var timeout = Timeout(timeout: 5) + var title = "" + @State var playing = true + @State private var appearSelf = true + + var body: some View { + if parent != nil { + GeometryReader { geo in + ZStack { + if(placeholder == nil) { + Image(systemName: "square") + .resizable() + .scaledToFill() + .foregroundColor(.black) + } + else { + placeholder!.resizable().scaledToFill() + } + if (appearSelf) + { + NavigationView{ + VStack{ + HStack{ + Button { + if ( parent!.player!.timeControlStatus == .playing ) { + parent!.player!.pause() + self.playing = false + } else { + parent!.player!.play() + self.playing = true + } + } label: { + ( + self.playing ? + Image(systemName: "stop") : + Image(systemName: "play") + ) + .resizable() + .scaledToFit() + .frame(width: geo.size.width/5.5) + }.background(Color(red: 0,green: 0,blue: 0,opacity: 0.2)) + .frame(width: geo.size.width/2.5) + .cornerRadius(90, antialiased: true) + .foregroundColor(.white) + .opacity(1) + .buttonStyle(.plain) + Button { + let curr = parent!.player!.currentItem + parent!.player!.advanceToNextItem() + curr!.seek(to: .zero) + parent!.player!.play() + self.playing = true + } label : { + Image(systemName: "chevron.forward") + .resizable() + .scaledToFit() + .frame(width: geo.size.width/7, height: geo.size.height/7) + }.background(Color.clear) + .clipShape(Circle()) + .foregroundColor(.white) + .frame(width: geo.size.width/4, height: geo.size.height/4) + .padding(0) + .opacity(1) + .buttonStyle(.plain) + } + }.onAppear(){ + DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: { + appearSelf = false + }) + }.navigationTitle("\(self.title)") + }.opacity(0.65).navigationBarBackButtonHidden(false) + } + }.onTapGesture { + appearSelf = true + DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: { + appearSelf = false + }) + } + } + } } - init() {} - init(parent:ContentView, music: IDStr) { - if music.art != nil { - self.placeholder = music.art! + init() { } + init(parent:ContentView, music: IDStr? = nil) { + if music != nil && music!.art != nil { + self.placeholder = music!.art! + self.music = music + self.parent = parent + self.playing = parent.player!.timeControlStatus == .playing } } + + mutating func update (music: IDStr) { + self.placeholder = music.art + self.music = music + self.title = music.s + self.playing = self.parent!.player!.timeControlStatus == .playing + } } struct PlaybackView_Previews: PreviewProvider { diff --git a/MusicPlayer Watch App/TODO.md b/MusicPlayer Watch App/TODO.md index d274003..39334b7 100644 --- a/MusicPlayer Watch App/TODO.md +++ b/MusicPlayer Watch App/TODO.md @@ -1,10 +1,21 @@ -# TODO +# Roadmap -- 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) - + - multiple datasources (smb, webdav, webserver(better regex)) +- A download view + - That shows files to download (can select which ones to sync) + - Download progress bar, pause/cancel/resume +- Optimize Detailed Control View + - Use customized controls + - Performance tuning, reuse one view object! + - Volume and Seekbar +- UI Improvements +- Playback functions Shuffle, Stop after next song, Repeat +- Support for multiple formats (flac, possibly others?) +- Database for caching +- Playlist, creation/management +- Lyrics +- Tidy up code, credit page, test on real devices and submit to the appstore diff --git a/MusicPlayer.xcodeproj/project.xcworkspace/xcuserdata/bill.xcuserdatad/xcdebugger/Expressions.xcexplist b/MusicPlayer.xcodeproj/project.xcworkspace/xcuserdata/bill.xcuserdatad/xcdebugger/Expressions.xcexplist index ab79c2a..4729816 100644 --- a/MusicPlayer.xcodeproj/project.xcworkspace/xcuserdata/bill.xcuserdatad/xcdebugger/Expressions.xcexplist +++ b/MusicPlayer.xcodeproj/project.xcworkspace/xcuserdata/bill.xcuserdatad/xcdebugger/Expressions.xcexplist @@ -8,6 +8,9 @@ + + diff --git a/MusicPlayer.xcodeproj/xcshareddata/xcschemes/MusicPlayer Watch App.xcscheme b/MusicPlayer.xcodeproj/xcshareddata/xcschemes/MusicPlayer Watch App.xcscheme new file mode 100644 index 0000000..2236bda --- /dev/null +++ b/MusicPlayer.xcodeproj/xcshareddata/xcschemes/MusicPlayer Watch App.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 1cc0737..929fe43 100644 --- a/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,4 +3,22 @@ uuid = "E40B0B2F-0F7C-4049-8384-96A6D265F1C3" type = "1" version = "2.0"> + + + + + + diff --git a/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcschemes/xcschememanagement.plist b/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcschemes/xcschememanagement.plist index 25f5f82..2665082 100644 --- a/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcschemes/xcschememanagement.plist @@ -10,5 +10,18 @@ 0 + SuppressBuildableAutocreation + + A95C117829C531C100737618 + + primary + + + A95C117E29C531C100737618 + + primary + + +