Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directory view #193

Merged
merged 10 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@ Doing so isn't fatal (it's not a secret), but it is annoying for other contribut

### Not yet released

* A directory view has been added, for users of servers that organize files by directory and prefer managing files that way.
* Files can be dropped onto the dock icon to import them.
* Empty artists entries are deleted from the local library on deleting downloaded items.
* Move request handling into an off-thread queue.
* Fix tracks not having a cover when imported.
* Fix imported tracks having the wrong bitrate shown in the inspector.
* Fix imported tracks not having a content type.
* Fix playlists not getting selected when navigating through history.

### Version 3.0

Expand Down
12 changes: 11 additions & 1 deletion Submariner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
3E04F5EB2B7043F600E24E56 /* SBMergeArtistsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E04F5EA2B7043F600E24E56 /* SBMergeArtistsController.swift */; };
3E04F5ED2B70487200E24E56 /* SBAddServerPlaylistController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E04F5EC2B70487200E24E56 /* SBAddServerPlaylistController.swift */; };
3E04F5F02B71D60F00E24E56 /* SBDirectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E04F5EF2B71D60F00E24E56 /* SBDirectory.swift */; };
3E04F5F22B71E33000E24E56 /* SBServerDirectoryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E04F5F12B71E33000E24E56 /* SBServerDirectoryController.swift */; };
3E0DAD5C29CA2A5600D895E2 /* SBTrackListLengthTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E0DAD5B29CA2A5600D895E2 /* SBTrackListLengthTransformer.swift */; };
3E189E4028EF5BAB0062ACA0 /* SBAudioMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E189E3F28EF5BAB0062ACA0 /* SBAudioMetadata.swift */; };
3E1B785E2ACE5039008927C6 /* SBInspectorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E1B785D2ACE5039008927C6 /* SBInspectorController.swift */; };
Expand Down Expand Up @@ -146,6 +148,9 @@
/* Begin PBXFileReference section */
3E04F5EA2B7043F600E24E56 /* SBMergeArtistsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBMergeArtistsController.swift; sourceTree = "<group>"; };
3E04F5EC2B70487200E24E56 /* SBAddServerPlaylistController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBAddServerPlaylistController.swift; sourceTree = "<group>"; };
3E04F5EE2B71D46E00E24E56 /* Submariner v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Submariner v5.xcdatamodel"; sourceTree = "<group>"; };
3E04F5EF2B71D60F00E24E56 /* SBDirectory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBDirectory.swift; sourceTree = "<group>"; };
3E04F5F12B71E33000E24E56 /* SBServerDirectoryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBServerDirectoryController.swift; sourceTree = "<group>"; };
3E0DAD5B29CA2A5600D895E2 /* SBTrackListLengthTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBTrackListLengthTransformer.swift; sourceTree = "<group>"; };
3E189E3228EBBAC40062ACA0 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
3E189E3F28EF5BAB0062ACA0 /* SBAudioMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBAudioMetadata.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -579,6 +584,7 @@
3EC03B2E29F4F2E0001FDE50 /* SBIndex.swift */,
3EC03B3029F4F2E0001FDE50 /* SBPlaylist.swift */,
3EC03B3229F4F2E0001FDE50 /* SBArtist.swift */,
3E04F5EF2B71D60F00E24E56 /* SBDirectory.swift */,
);
name = Concrete;
sourceTree = "<group>";
Expand Down Expand Up @@ -669,6 +675,7 @@
3E1B785D2ACE5039008927C6 /* SBInspectorController.swift */,
3E87E90D2B43557400E85000 /* SBServerSearchController.swift */,
3E7491962B6A1AE00052CBCE /* SBTracklistController.swift */,
3E04F5F12B71E33000E24E56 /* SBServerDirectoryController.swift */,
);
name = "View Controllers";
sourceTree = "<group>";
Expand Down Expand Up @@ -772,6 +779,7 @@
3EC03B4829F4F2E0001FDE50 /* SBNowPlaying.swift in Sources */,
4C87ED8E139CD91B0064DE2E /* SBWindowController.m in Sources */,
3E2155122B26E6F0004BCCFC /* SBSubsonicRequestType.swift in Sources */,
3E04F5F02B71D60F00E24E56 /* SBDirectory.swift in Sources */,
4C87EDA1139CD97B0064DE2E /* SBSplitView.m in Sources */,
3E87E9102B4364CF00E85000 /* Collection+IndexSet.swift in Sources */,
3E45201E29FAD41900604079 /* UserDefaults+Submariner.swift in Sources */,
Expand Down Expand Up @@ -827,6 +835,7 @@
4C7AA232139D57A00050BE95 /* SBMusicController.m in Sources */,
4C7AA24D139D64930050BE95 /* SBServerLibraryController.m in Sources */,
4CFAFC2C139EF08800E82B57 /* SBServerHomeController.m in Sources */,
3E04F5F22B71E33000E24E56 /* SBServerDirectoryController.swift in Sources */,
4CFAFC37139EF20600E82B57 /* MGScopeBar.m in Sources */,
4CFAFC38139EF20600E82B57 /* MGRecessedPopUpButtonCell.m in Sources */,
3E94E5FA2915AB130080FDF6 /* SBRoutePickerView.swift in Sources */,
Expand Down Expand Up @@ -1106,12 +1115,13 @@
4C87ED7B139CD8BE0064DE2E /* Submariner.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
3E04F5EE2B71D46E00E24E56 /* Submariner v5.xcdatamodel */,
3E6126EB2AD7363100B2A1E2 /* Submariner v4.xcdatamodel */,
3E5C41DB297F2E15009B9699 /* Submariner v3.xcdatamodel */,
3EA06A4E28B2C04B0091A75F /* Submariner v2.xcdatamodel */,
4C87ED7C139CD8BE0064DE2E /* Submariner.xcdatamodel */,
);
currentVersion = 3E6126EB2AD7363100B2A1E2 /* Submariner v4.xcdatamodel */;
currentVersion = 3E04F5EE2B71D46E00E24E56 /* Submariner v5.xcdatamodel */;
path = Submariner.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
Expand Down
4 changes: 2 additions & 2 deletions Submariner/PasteboardType+Submariner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Cocoa

extension NSPasteboard.PasteboardType {
/// Tracks from the library views.
static let libraryType = NSPasteboard.PasteboardType(rawValue: "SBLibraryTableViewDataType")
static let libraryType = NSPasteboard.PasteboardType(rawValue: "com.submarinerapp.item-url-list")
/// Tracks from the tracklist.
static let tracklistType = NSPasteboard.PasteboardType(rawValue: "SBTracklistTableViewDataType")
static let tracklistType = NSPasteboard.PasteboardType(rawValue: "com.submariner.tracklist-indices")
}
4 changes: 4 additions & 0 deletions Submariner/SBAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, catego
databaseController.showAlbums(sender)
}

@IBAction func showDirectories(_ sender: Any?) {
databaseController.showDirectories(sender)
}

@IBAction func showPodcasts(_ sender: Any?) {
databaseController.showPodcasts(sender)
}
Expand Down
3 changes: 3 additions & 0 deletions Submariner/SBDatabaseController.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
@class SBDownloadsController;
@class SBServerUserViewController;
@class SBServerSearchController;
@class SBServerDirectoryController;
@class SBInspectorController;
@class SBTracklistController;
@class SBPlaylistController;
Expand Down Expand Up @@ -111,6 +112,7 @@
SBMusicSearchController *musicSearchController;
SBServerLibraryController *serverLibraryController;
SBServerHomeController *serverHomeController;
SBServerDirectoryController *serverDirectoryController;
SBServerPodcastController *serverPodcastController;
SBServerUserViewController *serverUserController;
SBServerSearchController *serverSearchController;
Expand Down Expand Up @@ -180,6 +182,7 @@
- (IBAction)search:(id)sender;
- (IBAction)showIndices:(id)sender;
- (IBAction)showAlbums:(id)sender;
- (IBAction)showDirectories:(id)sender;
- (IBAction)showPodcasts:(id)sender;
- (IBAction)cleanTracklist:(id)sender;
- (IBAction)goToCurrentTrack:(id)sender;
Expand Down
20 changes: 19 additions & 1 deletion Submariner/SBDatabaseController.m
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ - (id)initWithManagedObjectContext:(NSManagedObjectContext *)context {
musicSearchController = [[SBMusicSearchController alloc] initWithManagedObjectContext:self.managedObjectContext];
serverLibraryController = [[SBServerLibraryController alloc] initWithManagedObjectContext:self.managedObjectContext];
serverHomeController = [[SBServerHomeController alloc] initWithManagedObjectContext:self.managedObjectContext];
serverDirectoryController = [[SBServerDirectoryController alloc] initWithManagedObjectContext:self.managedObjectContext];
serverPodcastController = [[SBServerPodcastController alloc] initWithManagedObjectContext:self.managedObjectContext];
serverUserController = [[SBServerUserViewController alloc] initWithManagedObjectContext:self.managedObjectContext];
serverSearchController = [[SBServerSearchController alloc] initWithManagedObjectContext:self.managedObjectContext];
Expand All @@ -140,6 +141,7 @@ - (id)initWithManagedObjectContext:(NSManagedObjectContext *)context {
[playlistController setDatabaseController:self];
[serverLibraryController setDatabaseController:self];
[serverHomeController setDatabaseController:self];
[serverDirectoryController setDatabaseController:self];
[serverSearchController setDatabaseController:self];
[serverUserController setDatabaseController:self];
[inspectorController setDatabaseController:self];
Expand Down Expand Up @@ -659,6 +661,7 @@ - (void)reloadServerInternal: (SBServer*)server {
}
[server getServerLicense];
[server getArtists];
[server getServerDirectories];
[server getServerPlaylists];
// XXX: Check if it's the current VC too?
if (server != nil && serverHomeController.server == server) {
Expand Down Expand Up @@ -845,6 +848,15 @@ - (IBAction)showAlbums:(id)sender {
[self navigateForwardToNavItem: navItem];
}

- (IBAction)showDirectories:(id)sender {
if (!self.server) {
return;
}
[self.server setSelectedTabIndex: 3];
SBNavigationItem *navItem = [[SBServerDirectoriesNavigationItem alloc] initWithServer: self.server];
[self navigateForwardToNavItem: navItem];
}

- (IBAction)showPodcasts:(id)sender {
if (!self.server) {
return;
Expand Down Expand Up @@ -1201,6 +1213,7 @@ - (void)setServer:(SBServer *)newServer {
editServerController.server = server;
addServerPlaylistController.server = server;
serverHomeController.server = server;
serverDirectoryController.server = server;
serverUserController.server = server;
serverSearchController.server = server;
serverLibraryController.server = server;
Expand Down Expand Up @@ -1272,7 +1285,9 @@ - (void)displayViewControllerForResource:(SBResource *)resource {
case 2:
navItem = [[SBServerPodcastsNavigationItem alloc] initWithServer: server];
break;
// 3 was search and 4 was server users
case 3:
navItem = [[SBServerDirectoriesNavigationItem alloc] initWithServer: server];
break;
}
} else if([resource isKindOfClass:[SBAlbum class]]) {
SBAlbum *album = (SBAlbum*)resource;
Expand Down Expand Up @@ -1847,6 +1862,8 @@ - (void)pageController:(NSPageController *)pageController didTransitionToObject:
SBPlaylist *playlist = playlistNavItem.playlist;
// set server for UI properly recognizes this
self.server = playlist.server;

[self updateSourceListSelection: playlist];
} else {
self.server = nil;
}
Expand Down Expand Up @@ -1955,6 +1972,7 @@ - (NSViewController *)pageController:(NSPageController *)pageController viewCont
@"Downloads": downloadsController,
@"ServerLibrary": serverLibraryController,
@"ServerHome": serverHomeController,
@"ServerDirectories": serverDirectoryController,
@"ServerPodcasts": serverPodcastController,
@"ServerSearch": serverSearchController,
@"MusicSearch": musicSearchController,
Expand Down
52 changes: 52 additions & 0 deletions Submariner/SBDirectory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// SBDirectory.swift
// Submariner
//
// Created by Calvin Buckley on 2024-02-05.
//
// Copyright (c) 2024 Calvin Buckley
// SPDX-License-Identifier: BSD-3-Clause
//

import Foundation
import CoreData

@objc(SBDirectory)
public class SBDirectory: SBMusicItem {
// #MARK: - Children wrapper

override public class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
if key == "children" {
return Set(["subdirectories", "tracks"])
} else if key == "tracks" {
return Set(["subdirectories", "children"])
} else if key == "subdirectories" {
return Set(["children", "tracks"])
}
return super.keyPathsForValuesAffectingValue(forKey: key)
}

// FIXME: keyPathsForValuesAffectingValue doesn't work as well as I'd hope with SwiftUI
@objc dynamic var children: [SBMusicItem] {
let directories = Array(self.subdirectories as? Set<SBDirectory> ?? Set())
.sorted {
let lhs = $0.itemName ?? ""
let rhs = $1.itemName ?? ""
return lhs.localizedCompare(rhs) == .orderedAscending
} as [SBMusicItem]
let tracks = Array(self.tracks as? Set<SBTrack> ?? Set())
.sorted {
let lhs = $0.path ?? ""
let rhs = $1.path ?? ""
return lhs.localizedCompare(rhs) == .orderedAscending
} as [SBMusicItem]
return directories + tracks
}

// #MARK: - Core Data insert compatibility shim

@objc(insertInManagedObjectContext:) class func insertInManagedObjectContext(context: NSManagedObjectContext) -> SBDirectory {
let entity = NSEntityDescription.entity(forEntityName: "Directory", in: context)
return NSEntityDescription.insertNewObject(forEntityName: entity!.name!, into: context) as! SBDirectory
}
}
4 changes: 4 additions & 0 deletions Submariner/SBNavigationItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import Cocoa
override var identifier: NSString { "ServerPodcasts" }
}

@objc class SBServerDirectoriesNavigationItem: SBServerNavigationItem {
override var identifier: NSString { "ServerDirectories" }
}

@objc class SBServerHomeNavigationItem: SBServerNavigationItem {
override var identifier: NSString { "ServerHome" }
}
Expand Down
18 changes: 18 additions & 0 deletions Submariner/SBServer+CoreDataProperties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ extension SBServer {
@NSManaged public var nowPlayings: NSSet?
@NSManaged public var home: SBHome?
@NSManaged public var indexes: NSSet?
@NSManaged public var directories: NSSet?

}

// MARK: Generated accessors for directories
extension SBServer {

@objc(addDirectoriesObject:)
@NSManaged public func addToDirectories(_ value: SBDirectory)

@objc(removeDirectoriesObject:)
@NSManaged public func removeFromDirectories(_ value: SBDirectory)

@objc(addDirectories:)
@NSManaged public func addToDirectories(_ values: NSSet)

@objc(removeDirectories:)
@NSManaged public func removeFromDirectories(_ values: NSSet)

}

Expand Down
Loading
Loading