Skip to content

Commit

Permalink
Added capability to import module files from device / network drive /…
Browse files Browse the repository at this point in the history
… cloud services (#11)

* Added import feature: possible to import modules that are not listed in AMP

* Fixed SamplePlayer demo app project dependencies

* Import allows composer name assignment to imported modules on a dialog

* Refactoring Import to use downloadController

* Import polisihing

* Import option added to playlist view (import to current playlist)

* Allow starting play for already imported module

* Readme, app_history and journal updated

* bump version to 3.3 (23)
  • Loading branch information
sitomani committed Aug 18, 2021
1 parent 246eade commit ff30c97
Show file tree
Hide file tree
Showing 37 changed files with 757 additions and 193 deletions.
14 changes: 8 additions & 6 deletions 4champ.xcodeproj/project.pbxproj
Expand Up @@ -166,7 +166,7 @@
456E6F2D240A5FAF0034BDA2 /* PlaylistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistView.swift; sourceTree = "<group>"; };
45855FC624444C9B0079C767 /* ampplayer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ampplayer.entitlements; sourceTree = "<group>"; };
45855FC8244467420079C767 /* DownloadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadView.swift; sourceTree = "<group>"; };
45855FCA244475050079C767 /* DownloadHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadHostingController.swift; sourceTree = "<group>"; };
45855FCA244475050079C767 /* DownloadHostingController.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = DownloadHostingController.swift; sourceTree = "<group>"; tabWidth = 2; };
45855FCC2444AC6B0079C767 /* ModuleSharing.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ModuleSharing.swift; sourceTree = "<group>"; tabWidth = 2; };
45A080E424327ED500AEA85F /* Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Entitlements.plist; sourceTree = "<group>"; };
45A4F130257EA970006FF21B /* liblibopenmpt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblibopenmpt.a; path = ../openmpt/liblibopenmpt.a; sourceTree = "<group>"; };
Expand Down Expand Up @@ -207,6 +207,7 @@
45D61F5A20E1780D00869814 /* Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Appearance.swift; sourceTree = "<group>"; };
45D61F5C20E17D8F00869814 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; };
45D61F6220E1863600869814 /* Gzip.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Gzip.framework; path = Carthage/Build/iOS/Gzip.framework; sourceTree = "<group>"; };
735A274D264FE9EA004EAC0C /* AmpCDModel 9 (imports).xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AmpCDModel 9 (imports).xcdatamodel"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -858,7 +859,7 @@
CODE_SIGN_ENTITLEMENTS = ampplayer.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 23;
DEVELOPMENT_TEAM = 2G92PSCBJ9;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
HEADER_SEARCH_PATHS = ../openmpt/libopenmpt;
Expand All @@ -869,7 +870,7 @@
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = ../openmpt;
MARKETING_VERSION = 3.2;
MARKETING_VERSION = 3.3;
PRODUCT_BUNDLE_IDENTIFIER = com.boogiesoftware.ampplayer;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -887,7 +888,7 @@
CODE_SIGN_ENTITLEMENTS = ampplayer.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 23;
DEVELOPMENT_TEAM = 2G92PSCBJ9;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
HEADER_SEARCH_PATHS = ../openmpt/libopenmpt;
Expand All @@ -898,7 +899,7 @@
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = ../openmpt;
MARKETING_VERSION = 3.2;
MARKETING_VERSION = 3.3;
PRODUCT_BUNDLE_IDENTIFIER = com.boogiesoftware.ampplayer;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -980,9 +981,10 @@
454D1AD822035D87006ABA85 /* AmpCDModel.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
735A274D264FE9EA004EAC0C /* AmpCDModel 9 (imports).xcdatamodel */,
454D1AD922035D87006ABA85 /* AmpCDModel 8 (preview).xcdatamodel */,
);
currentVersion = 454D1AD922035D87006ABA85 /* AmpCDModel 8 (preview).xcdatamodel */;
currentVersion = 735A274D264FE9EA004EAC0C /* AmpCDModel 9 (imports).xcdatamodel */;
path = AmpCDModel.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
Expand Down
3 changes: 2 additions & 1 deletion 4champ/AppDelegate.swift
Expand Up @@ -109,7 +109,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if let idString = url.path.split(separator: "/").first, let modId = Int(idString) {
dlController.show(modId: modId)
}

} else {
dlController.showImport(for: [url]);
}
return true
}
Expand Down
2 changes: 1 addition & 1 deletion 4champ/Data/AmpCDModel.xcdatamodeld/.xccurrentversion
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>AmpCDModel 8 (preview).xcdatamodel</string>
<string>AmpCDModel 9 (imports).xcdatamodel</string>
</dict>
</plist>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="19H15" minimumToolsVersion="Xcode 4.1" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="ModuleInfo" representedClassName="ModuleInfo" syncable="YES">
<attribute name="added" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="lastPlayed" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="modAuthor" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="modDLStatus" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="modFavorite" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
<attribute name="modId" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="modLocalPath" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="modName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="modSize" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="modType" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="modURL" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="playCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="preview" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
<attribute name="radioOnly" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serviceId" attributeType="Integer 16" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serviceKey" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="shared" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<relationship name="playlists" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Playlist" inverseName="modules" inverseEntity="Playlist" syncable="YES"/>
</entity>
<entity name="Playlist" representedClassName="Playlist" syncable="YES">
<attribute name="locked" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="playhead" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="playmode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="plId" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="plName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="position" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<relationship name="modules" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ModuleInfo" inverseName="playlists" inverseEntity="ModuleInfo" syncable="YES"/>
</entity>
<elements>
<element name="ModuleInfo" positionX="0" positionY="0" width="128" height="313"/>
<element name="Playlist" positionX="0" positionY="0" width="128" height="150"/>
</elements>
</model>
4 changes: 3 additions & 1 deletion 4champ/Data/ModuleInfo+CoreDataProperties.swift
Expand Up @@ -32,7 +32,9 @@ extension ModuleInfo: Identifiable {
@NSManaged public var radioOnly: NSNumber?
@NSManaged public var shared: NSDate?
@NSManaged public var playlists: NSSet?

@NSManaged public var serviceId: NSNumber?
@NSManaged public var serviceKey: String?

public var id: Int {
return modId?.intValue ?? 0
}
Expand Down
12 changes: 11 additions & 1 deletion 4champ/Data/Structs.swift
Expand Up @@ -8,6 +8,12 @@
import Foundation
import UIKit

/// Enumeration identifying the source of a module
enum ModuleService: Int {
case local = 0
case amp = 1
}

struct Constants {
static let radioBufferLen = 3 // Length of the radio buffer
static let searchDelay = 0.3 // Type wait delay before search is triggered
Expand Down Expand Up @@ -41,6 +47,8 @@ struct MMD: Identifiable {
self.name = cdi.modName
self.size = cdi.modSize?.intValue
self.type = cdi.modType
self.serviceId = ModuleService.init(rawValue: cdi.serviceId?.intValue ?? 1) ?? .amp
self.serviceKey = cdi.serviceKey
self.favorite = cdi.modFavorite?.boolValue ?? false
}

Expand Down Expand Up @@ -70,7 +78,9 @@ struct MMD: Identifiable {
var localPath: URL?
var downloadPath: URL?
var favorite: Bool = false
var note: String?
var note: String?
var serviceId: ModuleService? // local | amp | nn?
var serviceKey: String? // identifier of the module in non-amp service
func fileExists() -> Bool {
if let path = localPath?.path {
return FileManager.default.fileExists(atPath: path)
Expand Down
86 changes: 86 additions & 0 deletions 4champ/Info.plist
Expand Up @@ -6,6 +6,21 @@
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>4champ</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Tracker music file</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.4champ.trackermusicfile</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
Expand Down Expand Up @@ -33,6 +48,8 @@
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
Expand Down Expand Up @@ -91,7 +108,76 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportsDocumentBrowser</key>
<false/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Tracker music file</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>com.4champ.trackermusicfile</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>669</string>
<string>AMF</string>
<string>AMS</string>
<string>DBM</string>
<string>DIGI</string>
<string>DMF</string>
<string>DSM</string>
<string>DTM</string>
<string>FAR</string>
<string>IT</string>
<string>GDM</string>
<string>ST26</string>
<string>IMF</string>
<string>J2B</string>
<string>M15</string>
<string>MED</string>
<string>MDL</string>
<string>MOD</string>
<string>MTM</string>
<string>NST</string>
<string>OCT</string>
<string>OKT</string>
<string>OSS</string>
<string>PTM</string>
<string>PSM</string>
<string>S3M</string>
<string>STM</string>
<string>SFX</string>
<string>SFX2</string>
<string>ULT</string>
<string>UMX</string>
<string>WOW</string>
<string>XM</string>
<string>FST</string>
<string>STK</string>
<string>MMCMP</string>
<string>MMS</string>
<string>MO3</string>
<string>MPTM</string>
<string>OK</string>
<string>PLM</string>
<string>PPM</string>
<string>PT36</string>
<string>AHX</string>
<string>THX</string>
<string>HVL</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>
1 change: 1 addition & 0 deletions 4champ/Networking/ModuleFetcher.swift
Expand Up @@ -144,6 +144,7 @@ class ModuleFetcher {
} catch {
log.error("Could not write module data to file: \(error)")
}
mmd.serviceId = .amp
self.state = .done(mmd: mmd)
self.state = .idle
self.currentRequest = nil
Expand Down
10 changes: 7 additions & 3 deletions 4champ/Replay/ModulePlayer.swift
Expand Up @@ -164,9 +164,9 @@ class ModulePlayer: NSObject {
/// - parameters:
/// - at: index of the module to play in the playlist.
/// Invalid index will result in no change in playback.
func play(at: Int) {
@discardableResult func play(at: Int) -> Bool {
guard at < playQueue.count, let path = playQueue[at].localPath?.path else {
return
return false
}
renderer.stop()
if renderer.loadModule(path, type:playQueue[at].type) {
Expand All @@ -176,11 +176,13 @@ class ModulePlayer: NSObject {
currentModule = playQueue[at]
renderer.play()
status = .playing
return true
} else {
log.error("Could not load tune: \(path)")
_ = observers.map {
$0.errorOccurred(error: .loadFailed(mmd: playQueue[at]))
}
return false
}
}

Expand Down Expand Up @@ -221,7 +223,9 @@ class ModulePlayer: NSObject {
while playQueue[nextIndex] == currentModule && nextIndex < playQueue.count-1 {
nextIndex += 1
}
play(at: nextIndex)
if play(at: nextIndex) == false {
playQueue.remove(at: nextIndex)
}
}

/// Plays the previous module in the current playlist. The playlist index
Expand Down

0 comments on commit ff30c97

Please sign in to comment.