master
Bill 1 year ago
parent 6c6af5a7e6
commit 42598d11f5

@ -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(#".*(<a\s+href=\"(.*.m4a)\">)"#)
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!))
}
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)
}
}*/
}
}
}

@ -7,7 +7,6 @@
import SwiftUI
import WatchKit
import UserNotifications
@main
struct MusicPlayer_Watch_AppApp: App {

@ -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 {

@ -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

@ -8,6 +8,9 @@
<PersistentString
value = "match.output[0].substring!.cString(using: String.Encoding.utf8)">
</PersistentString>
<PersistentString
value = "String(match.output[2].substring!)">
</PersistentString>
</PersistentStrings>
</ContextState>
</ContextStates>

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95C117E29C531C100737618"
BuildableName = "MusicPlayer Watch App.app"
BlueprintName = "MusicPlayer Watch App"
ReferencedContainer = "container:MusicPlayer.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95C117829C531C100737618"
BuildableName = "MusicPlayer.app"
BlueprintName = "MusicPlayer"
ReferencedContainer = "container:MusicPlayer.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
enableASanStackUseAfterReturn = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95C117E29C531C100737618"
BuildableName = "MusicPlayer Watch App.app"
BlueprintName = "MusicPlayer Watch App"
ReferencedContainer = "container:MusicPlayer.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95C117E29C531C100737618"
BuildableName = "MusicPlayer Watch App.app"
BlueprintName = "MusicPlayer Watch App"
ReferencedContainer = "container:MusicPlayer.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

@ -3,4 +3,22 @@
uuid = "E40B0B2F-0F7C-4049-8384-96A6D265F1C3"
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "97AD3DB1-A549-419A-B226-A00CBDB192CF"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "MusicPlayer Watch App/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "135"
endingLineNumber = "135"
landmarkName = "init()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

@ -10,5 +10,18 @@
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>A95C117829C531C100737618</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>A95C117E29C531C100737618</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

Loading…
Cancel
Save