diff --git a/.github/workflows/objective-c-xcode.yml b/.github/workflows/objective-c-xcode.yml new file mode 100644 index 0000000..4c11190 --- /dev/null +++ b/.github/workflows/objective-c-xcode.yml @@ -0,0 +1,56 @@ +name: Build and Archive iOS Project + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: macos-latest + + steps: + # Шаг 1: Клонирование репозитория + - name: Checkout source code + uses: actions/checkout@v3 + + # Шаг 2: Настройка Xcode + - name: Set up Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: 16.1 + + # Шаг 3: Сборка xcarchive + - name: Build xcarchive + run: | + xcodebuild clean archive \ + -project EasyMusic.xcodeproj \ + -scheme EasyMusic \ + -configuration Release \ + -archivePath ./build/EasyMusic.xcarchive \ + -destination "generic/platform=iOS" \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_ALLOWED=NO + - name: Check archive contents + run: | + ls -la ./build/EasyMusic.xcarchive + ls -la ./build/EasyMusic.xcarchive/Products/Applications + ls -la ./build/EasyMusic.xcarchive/dSYMs + # Шаг 4: Сохранение xcarchive как артефакт + - name: Upload xcarchive as artifact + uses: actions/upload-artifact@v3 + with: + name: EasyMusic.xcarchive + path: ./build/EasyMusic.xcarchive + + # Шаг 5: Добавление инструкции по созданию .ipa + - name: Upload instructions + run: echo "To create .ipa:\n1. Open Xcode Organizer or use xcodebuild.\n2. Use xcarchive to generate .ipa.\n3. For manual export, run:\nxcodebuild -exportArchive -archivePath ./EasyMusic.xcarchive -exportPath ./EasyMusic.ipa -exportOptionsPlist ./ExportOptions.plist" > README_FOR_IPA.txt + - name: Upload README + uses: actions/upload-artifact@v3 + with: + name: README_FOR_IPA + path: README_FOR_IPA.txt diff --git a/.github/workflows/xuy.yml b/.github/workflows/xuy.yml new file mode 100644 index 0000000..75ecfcc --- /dev/null +++ b/.github/workflows/xuy.yml @@ -0,0 +1,16 @@ +name: Work Stats Readme + +on: + workflow_dispatch: + schedule: + # Runs every 2 hours + - cron: "0 */9 * * *" + +jobs: + update-readme: + name: Update this repo's README + runs-on: ubuntu-latest + steps: + - uses: athul/waka-readme@master + with: + WAKATIME_API_KEY: ${{ secrets.WAKATIME_API_KEY }} diff --git a/EasyMusic.xcodeproj/project.pbxproj b/EasyMusic.xcodeproj/project.pbxproj index 938cfb6..c490faa 100644 --- a/EasyMusic.xcodeproj/project.pbxproj +++ b/EasyMusic.xcodeproj/project.pbxproj @@ -30,7 +30,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - F0CD7D9B2D059D7B0034CC02 /* Exceptions for "EasyMusic" folder in "EasyMusic" target */ = { + F0D851512D064F7E001A76ED /* Exceptions for "EasyMusic" folder in "EasyMusic" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, @@ -43,20 +43,11 @@ F0CD7D6D2D059CED0034CC02 /* EasyMusic */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - F0CD7D9B2D059D7B0034CC02 /* Exceptions for "EasyMusic" folder in "EasyMusic" target */, + F0D851512D064F7E001A76ED /* Exceptions for "EasyMusic" folder in "EasyMusic" target */, ); - path = EasyMusic; - sourceTree = ""; - }; - F0CD7D7E2D059CEE0034CC02 /* EasyMusicTests */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = EasyMusicTests; - sourceTree = ""; - }; - F0CD7D882D059CEE0034CC02 /* EasyMusicUITests */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = EasyMusicUITests; - sourceTree = ""; + name = EasyMusic; + path = /Users/arbung/iOS/calcul/platonfm/EasyMusic/EasyMusic; + sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ @@ -89,8 +80,6 @@ isa = PBXGroup; children = ( F0CD7D6D2D059CED0034CC02 /* EasyMusic */, - F0CD7D7E2D059CEE0034CC02 /* EasyMusicTests */, - F0CD7D882D059CEE0034CC02 /* EasyMusicUITests */, F0CD7D6C2D059CED0034CC02 /* Products */, ); sourceTree = ""; @@ -143,9 +132,6 @@ dependencies = ( F0CD7D7D2D059CEE0034CC02 /* PBXTargetDependency */, ); - fileSystemSynchronizedGroups = ( - F0CD7D7E2D059CEE0034CC02 /* EasyMusicTests */, - ); name = EasyMusicTests; packageProductDependencies = ( ); @@ -166,9 +152,6 @@ dependencies = ( F0CD7D872D059CEE0034CC02 /* PBXTargetDependency */, ); - fileSystemSynchronizedGroups = ( - F0CD7D882D059CEE0034CC02 /* EasyMusicUITests */, - ); name = EasyMusicUITests; packageProductDependencies = ( ); @@ -409,7 +392,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 23; DEVELOPMENT_ASSET_PATHS = "\"EasyMusic/Preview Content\""; DEVELOPMENT_TEAM = 25G2F7AWV6; ENABLE_PREVIEWS = YES; @@ -419,6 +402,7 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UIStatusBarHidden = NO; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -426,7 +410,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2; + MARKETING_VERSION = 1.6; PRODUCT_BUNDLE_IDENTIFIER = platon.EasyMusic; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -441,7 +425,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 23; DEVELOPMENT_ASSET_PATHS = "\"EasyMusic/Preview Content\""; DEVELOPMENT_TEAM = 25G2F7AWV6; ENABLE_PREVIEWS = YES; @@ -451,6 +435,7 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UIStatusBarHidden = NO; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -458,7 +443,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2; + MARKETING_VERSION = 1.6; PRODUCT_BUNDLE_IDENTIFIER = platon.EasyMusic; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/EasyMusic.xcodeproj/project.xcworkspace/xcuserdata/arbung.xcuserdatad/UserInterfaceState.xcuserstate b/EasyMusic.xcodeproj/project.xcworkspace/xcuserdata/arbung.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..ad020bf Binary files /dev/null and b/EasyMusic.xcodeproj/project.xcworkspace/xcuserdata/arbung.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/EasyMusic/AppSettings.swift b/EasyMusic/AppSettings.swift new file mode 100644 index 0000000..5c63f23 --- /dev/null +++ b/EasyMusic/AppSettings.swift @@ -0,0 +1,13 @@ +// +// AppSettings.swift +// EasyMusic +// +// Created by Platon on 10.12.2024. +// + + +import SwiftUI + +class AppSettings: ObservableObject { + @Published var useNewDesign: Bool = false +} diff --git a/EasyMusic/Assets.xcassets/AppIcon.appiconset/Contents.json b/EasyMusic/Assets.xcassets/AppIcon.appiconset/Contents.json index 926f6e1..84c6cfe 100644 --- a/EasyMusic/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/EasyMusic/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -13,7 +13,7 @@ "value" : "dark" } ], - "filename" : "asdasdasd.jpg", + "filename" : "newtinted 1.jpg", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -25,7 +25,7 @@ "value" : "tinted" } ], - "filename" : "gkjhkjhkj.jpg", + "filename" : "newtinted.jpg", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/EasyMusic/Assets.xcassets/AppIcon.appiconset/asdasdasd.jpg b/EasyMusic/Assets.xcassets/AppIcon.appiconset/asdasdasd.jpg deleted file mode 100644 index 6fd1efc..0000000 Binary files a/EasyMusic/Assets.xcassets/AppIcon.appiconset/asdasdasd.jpg and /dev/null differ diff --git a/EasyMusic/Assets.xcassets/AppIcon.appiconset/gkjhkjhkj.jpg b/EasyMusic/Assets.xcassets/AppIcon.appiconset/gkjhkjhkj.jpg deleted file mode 100644 index 0951d36..0000000 Binary files a/EasyMusic/Assets.xcassets/AppIcon.appiconset/gkjhkjhkj.jpg and /dev/null differ diff --git a/EasyMusic/Assets.xcassets/AppIcon.appiconset/newtinted 1.jpg b/EasyMusic/Assets.xcassets/AppIcon.appiconset/newtinted 1.jpg new file mode 100644 index 0000000..fdae70f Binary files /dev/null and b/EasyMusic/Assets.xcassets/AppIcon.appiconset/newtinted 1.jpg differ diff --git a/EasyMusic/Assets.xcassets/AppIcon.appiconset/newtinted.jpg b/EasyMusic/Assets.xcassets/AppIcon.appiconset/newtinted.jpg new file mode 100644 index 0000000..fdae70f Binary files /dev/null and b/EasyMusic/Assets.xcassets/AppIcon.appiconset/newtinted.jpg differ diff --git a/EasyMusic/ClassicDesignView.swift b/EasyMusic/ClassicDesignView.swift new file mode 100644 index 0000000..a441b35 --- /dev/null +++ b/EasyMusic/ClassicDesignView.swift @@ -0,0 +1,35 @@ +// +// ClassicDesignView.swift +// EasyMusic +// +// Created by Platon on 10.12.2024. +// + +import SwiftUI + +struct ClassicDesignView: View { + var body: some View { + List(stations, id: \.name) { station in + NavigationLink(destination: RadioPlayer(station: station)) { + HStack { + Circle() + .fill(LinearGradient( + gradient: Gradient(colors: station.colors), + startPoint: .top, + endPoint: .bottom + )) + .frame(width: 50, height: 50) + + VStack(alignment: .leading) { + Text(station.name) + .font(.headline) + Text(station.description) + .font(.subheadline) + .lineLimit(2) + } + } + } + } + .navigationTitle("Каналы") + } +} diff --git a/EasyMusic/ContentView.swift b/EasyMusic/ContentView.swift index c39ed3b..fb7bd47 100644 --- a/EasyMusic/ContentView.swift +++ b/EasyMusic/ContentView.swift @@ -8,122 +8,27 @@ import SwiftUI struct ContentView: View { - @State private var currentStationIndex: Int = 0 - @State private var isPlaying: Bool = false - @ObservedObject private var radioPlayer = RadioPlayer.shared - - private let stations: [Station] = [ - Station( - name: "Tabris FM", - description: "Радио, на котором крутятся отобранные, нормальные, приятные треки многих жанров. Плейлист пополняется ежедневно в районе от 18:00 до 22:00.", - streamURL: "https://stream.zeno.fm/uzrnuzqmen6tv", - color: LinearGradient(colors: [.red], startPoint: .top, endPoint: .bottom) - ), - Station( - name: "Night FM", - description: "Радио для концентрации, успокоения, наслаждения. На радио играет спокойная музыка различных исполнителей. Идеально подойдет для ночных прогулок и поездок.", - streamURL: "https://stream.zeno.fm/lgxpsux5v9avv", - color: LinearGradient(colors: [.blue, .cyan], startPoint: .top, endPoint: .bottom) - ), - Station( - name: "Penis FM", - description: "Радио для тех, кого обычные треки не устраивают и они слушают альтернативные жанры. На радио играют треки таких исполнителей как Dekma, Кишлак и т.д.", - streamURL: "https://stream.zeno.fm/hfrwlmkuux4uv", - color: LinearGradient(colors: [.blue, .purple], startPoint: .top, endPoint: .bottom) - ), - Station( - name: "Platon FM", - description: "Музыка всех жанров, характеров и исполнителей. Идеально подойдет для меломанов.", - streamURL: "http://45.95.234.91:8000/music", - color: LinearGradient(colors: [.yellow, .orange], startPoint: .top, endPoint: .bottom) - ) - ] - - private var currentStation: Station { - stations[currentStationIndex] - } + @StateObject private var appSettings = AppSettings() var body: some View { - ZStack { - currentStation.color - .ignoresSafeArea() - - VStack(spacing: 20) { - Text(currentStation.name) - .font(.largeTitle) - .fontWeight(.bold) - .foregroundColor(.white) - - Text(currentStation.description) - .multilineTextAlignment(.center) - .foregroundColor(.white) - .padding() - - Text("Now Playing: \(radioPlayer.trackTitle)") - .foregroundColor(.white) - .font(.headline) - - Spacer() - - VStack { - Button(action: togglePlay) { - Image(systemName: isPlaying ? "pause.circle.fill" : "play.circle.fill") - .resizable() - .frame(width: 80, height: 80) - .foregroundColor(.white) - .padding(.bottom, 20) - } - - HStack { - Button(action: previousStation) { - Image(systemName: "chevron.left") - .resizable() - .frame(width: 40, height: 40) - .foregroundColor(.white) - } - Spacer() - Button(action: nextStation) { - Image(systemName: "chevron.right") - .resizable() - .frame(width: 40, height: 40) - .foregroundColor(.white) - } - } - .padding(.horizontal, 40) + NavigationView { + VStack { + if appSettings.useNewDesign { + NewDesignView() + } else { + ClassicDesignView() } } - .padding() - } - .onAppear { - NotificationCenter.default.addObserver(forName: .nextStation, object: nil, queue: .main) { _ in - self.nextStation() - } - NotificationCenter.default.addObserver(forName: .previousStation, object: nil, queue: .main) { _ in - self.previousStation() + .navigationTitle("EasyMusic") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + NavigationLink(destination: SettingsView(appSettings: appSettings)) { + Image(systemName: "gearshape") + .imageScale(.large) + .foregroundColor(.blue) + } + } } } } - - private func togglePlay() { - if isPlaying { - radioPlayer.stop() - } else { - radioPlayer.playStream(url: currentStation.streamURL) - } - isPlaying.toggle() - } - - private func previousStation() { - if currentStationIndex > 0 { - currentStationIndex -= 1 - radioPlayer.playStream(url: currentStation.streamURL) - } - } - - private func nextStation() { - if currentStationIndex < stations.count - 1 { - currentStationIndex += 1 - radioPlayer.playStream(url: currentStation.streamURL) - } - } } diff --git a/EasyMusic/Info.plist b/EasyMusic/Info.plist index bc52852..4577eff 100644 --- a/EasyMusic/Info.plist +++ b/EasyMusic/Info.plist @@ -4,8 +4,16 @@ NSAppTransportSecurity - NSAllowsArbitraryLoads - + NSExceptionDomains + + 45.95.234.91 + + NSExceptionAllowsInsecureHTTPLoads + + NSIncludesSubdomains + + + UIBackgroundModes diff --git a/EasyMusic/NewDesignView.swift b/EasyMusic/NewDesignView.swift new file mode 100644 index 0000000..65e2098 --- /dev/null +++ b/EasyMusic/NewDesignView.swift @@ -0,0 +1,50 @@ +// +// NewDesignView.swift +// EasyMusic +// +// Created by Platon on 10.12.2024. +// + +import SwiftUI + +struct NewDesignView: View { + @State private var currentStation: Station = stations[0] + @State private var isPlaying: Bool = false + + var body: some View { + VStack { + Spacer() + + Button(action: { + isPlaying.toggle() + // Add animation or music playback toggle here + }) { + ZStack { + Circle() + .fill(LinearGradient( + gradient: Gradient(colors: currentStation.colors), + startPoint: .top, + endPoint: .bottom + )) + .frame(width: 100, height: 100) + + Image(systemName: isPlaying ? "pause.circle" : "play.circle") + .resizable() + .frame(width: 60, height: 60) + .foregroundColor(.white) + } + } + + Spacer() + + Button("здесь пока ничего не работает, пожалуйста, переключись обратно в старый дизайн") { + // Add settings navigation logic + } + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(8) + } + .navigationTitle(currentStation.name) + } +} diff --git a/EasyMusic/RadioPlayer.swift b/EasyMusic/RadioPlayer.swift index a50bc6a..f726a5e 100644 --- a/EasyMusic/RadioPlayer.swift +++ b/EasyMusic/RadioPlayer.swift @@ -6,128 +6,49 @@ // -import AVFoundation -import MediaPlayer +import SwiftUI +import AVKit -class RadioPlayer: NSObject, ObservableObject { - static let shared = RadioPlayer() - private var player: AVPlayer? +struct RadioPlayer: View { + let station: Station + @State private var player = AVPlayer() - @Published var trackTitle: String = "Unknown Track" + var body: some View { + VStack { + Text(station.name) + .font(.largeTitle) + .padding() - override init() { - super.init() - setupRemoteTransportControls() - } - - func playStream(url: String) { - guard let streamURL = URL(string: url) else { - print("Invalid stream URL") - return - } - print("Playing stream: \(url)") - let playerItem = AVPlayerItem(url: streamURL) - player = AVPlayer(playerItem: playerItem) - - // Подписываемся на обновления метаданных - playerItem.addObserver(self, forKeyPath: "timedMetadata", options: [.new, .initial], context: nil) - player?.play() + Text(station.description) + .padding() - do { - try AVAudioSession.sharedInstance().setActive(true) - } catch { - print("Failed to activate audio session: \(error)") - } - - updateNowPlayingInfo(title: "Connecting...", artist: "EasyMusic") - } + Spacer() - func stop() { - print("Stopping playback") - player?.pause() - player?.currentItem?.removeObserver(self, forKeyPath: "timedMetadata") - player = nil - MPNowPlayingInfoCenter.default().nowPlayingInfo = nil - } - - override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { - if keyPath == "timedMetadata", - let playerItem = object as? AVPlayerItem, - let metadataList = playerItem.timedMetadata { - for metadata in metadataList { - if let value = metadata.value as? String { - let cleanedValue = cleanMetadata(value) - DispatchQueue.main.async { - self.trackTitle = cleanedValue - print("Now playing: \(self.trackTitle)") - self.updateNowPlayingInfo(title: self.trackTitle, artist: "EasyMusic") - } - } + Button(action: togglePlay) { + Image(systemName: player.timeControlStatus == .playing ? "pause.circle.fill" : "play.circle.fill") + .resizable() + .frame(width: 100, height: 100) + .foregroundColor(.blue) } - } - } - - private func setupRemoteTransportControls() { - let commandCenter = MPRemoteCommandCenter.shared() - - commandCenter.playCommand.isEnabled = true - commandCenter.playCommand.addTarget { [weak self] event in - self?.resumePlayback() - return .success - } - commandCenter.pauseCommand.isEnabled = true - commandCenter.pauseCommand.addTarget { [weak self] event in - self?.pausePlayback() - return .success + Spacer() } - - commandCenter.stopCommand.isEnabled = true - commandCenter.stopCommand.addTarget { [weak self] event in - self?.stop() - return .success + .onAppear { + setupPlayer() } } - private func resumePlayback() { - guard let player = player else { return } - player.play() - updateNowPlayingInfo(title: trackTitle, artist: "EasyMusic") - } - - private func pausePlayback() { - guard let player = player else { return } - player.pause() + private func setupPlayer() { + guard let url = URL(string: station.streamURL) else { return } + let item = AVPlayerItem(url: url) + player.replaceCurrentItem(with: item) } - private func updateNowPlayingInfo(title: String, artist: String) { - var nowPlayingInfo: [String: Any] = [ - MPMediaItemPropertyTitle: title, - MPMediaItemPropertyArtist: artist - ] - - if let artworkImage = UIImage(systemName: "music.note") { - let artwork = MPMediaItemArtwork(boundsSize: artworkImage.size) { _ in artworkImage } - nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork + private func togglePlay() { + if player.timeControlStatus == .playing { + player.pause() + } else { + player.play() } - - nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player?.rate ?? 0 - nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = true - - MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } - - private func cleanMetadata(_ rawValue: String) -> String { - if let data = rawValue.data(using: .isoLatin1), let utf8String = String(data: data, encoding: .utf8) { - return utf8String.trimmingCharacters(in: .whitespacesAndNewlines) - } - return rawValue.trimmingCharacters(in: .whitespacesAndNewlines) - } -} - -import Foundation - -extension Notification.Name { - static let nextStation = Notification.Name("nextStation") - static let previousStation = Notification.Name("previousStation") } diff --git a/EasyMusic/SettingsView.swift b/EasyMusic/SettingsView.swift new file mode 100644 index 0000000..37fd84c --- /dev/null +++ b/EasyMusic/SettingsView.swift @@ -0,0 +1,42 @@ +// +// SettingsView.swift +// EasyMusic +// +// Created by Platon on 10.12.2024. +// + +import SwiftUI + +struct SettingsView: View { + @ObservedObject var appSettings: AppSettings + @State private var developerClickCount = 0 + @State private var isDeveloperModeEnabled = false + + var body: some View { + VStack { + Toggle(isOn: $appSettings.useNewDesign) { + Text("Использовать новый дизайн") + } + .padding() + + Text("Версия: 2.0.0") + .onTapGesture { + developerClickCount += 1 + if developerClickCount == 7 { + isDeveloperModeEnabled = true + } + } + .padding() + + if isDeveloperModeEnabled { + Button(action: { + fatalError("Краш тест") + }) { + Text("Крашнуть") // The app is fucking die and you have to re-open it several times + .foregroundColor(.red) + } + } + } + .navigationTitle("Настройки") + } +} diff --git a/EasyMusic/Station.swift b/EasyMusic/Station.swift index b56ea1a..ae2c76c 100644 --- a/EasyMusic/Station.swift +++ b/EasyMusic/Station.swift @@ -8,10 +8,42 @@ import SwiftUI -struct Station: Identifiable { - let id = UUID() +struct Station: Hashable { let name: String let description: String let streamURL: String - let color: LinearGradient + let colors: [Color] } + +let stations: [Station] = [ + Station( + name: "Tabris FM", + description: "Радио, на котором крутятся отобранные, нормальные, приятные треки многих жанров. Плейлист пополняется ежедневно в районе от 18:00 до 22:00.", + streamURL: "https://stream.zeno.fm/uzrnuzqmen6tv", + colors: [.red, .orange] + ), + Station( + name: "Night FM", + description: "Радио для концентрации, успокоения, наслаждения. На радио играет спокойная музыка различных исполнителей. Идеально подойдёт для ночных прогулок и поездок.", + streamURL: "https://stream.zeno.fm/lgxpsux5v9avv", + colors: [.blue, .cyan] + ), + Station( + name: "Penis FM", + description: "Радио для тех, кого обычные треки не устраивают, и они слушают альтернативные жанры. На радио играют треки таких исполнителей, как Dekma, Кишлак и т.д.", + streamURL: "https://stream.zeno.fm/hfrwlmkuux4uv", + colors: [.blue, .purple] + ), + Station( + name: "Platon FM", + description: "Музыка всех жанров, характеров и исполнителей. Идеально подойдёт для меломанов.", + streamURL: "http://45.95.234.91:8000/music", + colors: [.yellow, .orange] + ), + Station( + name: "Memschol FM", + description: "Полная сборная солянка от красивых и уникальных жанров до рофл гей ремиксов и блатных треков. Часто проводятся подкасты на разные темы в прямом эфире от простого общения до политики.", + streamURL: "https://stream.zeno.fm/hydtchh8maguv.m3u", + colors: [Color(red: 106/255, green: 13/255, blue: 173/255), .purple] + ) +] diff --git a/EasyMusic/StationPickerView.swift b/EasyMusic/StationPickerView.swift new file mode 100644 index 0000000..77b2f94 --- /dev/null +++ b/EasyMusic/StationPickerView.swift @@ -0,0 +1,139 @@ +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +//There should have been some application logic here, but I was too lazy to rewrite it. +// +// StationPickerView.swift +// EasyMusic +// +// Created by Platon on 10.12.2024. +// + + +// import SwiftUI + +//struct StationPickerView: View { +// @ObservedObject var appSettings: AppSettings +// +// var body: some View { +// NavigationView { +// List { +// ForEach(stations, id: \.self) { station in +// Button(action: { +// appSettings.selectedStation = station // Назначение выбранной станции +// }) { +// HStack { +// Circle() +// .fill(LinearGradient( +// gradient: Gradient(colors: station.colors), +// startPoint: .top, +// endPoint: .bottom +// )) +// .frame(width: 30, height: 30) +// +// Text(station.name) +// .font(.headline) +// } +// } +// } +// } +// .navigationTitle("Выбор станции") +// } +// } +//} diff --git a/ExportOptions.plist b/ExportOptions.plist new file mode 100644 index 0000000..8036e6f --- /dev/null +++ b/ExportOptions.plist @@ -0,0 +1,16 @@ + + + + + method + development + stripSwiftSymbols + + compileBitcode + + signingStyle + manual + destination + export + + diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..bc52852 --- /dev/null +++ b/Info.plist @@ -0,0 +1,17 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIBackgroundModes + + audio + fetch + processing + + + diff --git a/README.md b/README.md index 4ffed47..3d225ae 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ # EasyMusic -ватсфин чат опенсурс проперти +## ватсфин чат опенсурс проперти + +# +На данный момент разработчик: @platonoferon +# +Канал проекта @easymusiccom +# + +# Сколько я работаю над кодом: + + +```txt +Other 10 hrs 18 mins ███████████████████████▒░ 93.75 % +Swift 41 mins █▓░░░░░░░░░░░░░░░░░░░░░░░ 06.25 % +``` + + +