Archived
1
This repository has been archived on 2020-06-04. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tagtunes/TagTunes/TagTunesTrack.swift
Kim Wittenburg 0a485ff42a Stuff…
2019-02-01 22:59:01 +01:00

286 lines
8.8 KiB
Swift
Executable File

//
// Track.swift
// TagTunes
//
// Created by Kim Wittenburg on 17.03.16.
// Copyright © 2016 Kim Wittenburg. All rights reserved.
//
import SearchAPI
// TODO: Documentation
public enum TagTunesTrackErrors: ErrorType {
case FileNotFound
case FileNotReadable
case NoIDFound
}
/// Represents a track in TagTunes. A track is directly related to a file. There
/// may be different subclasses of this class representing different kinds of
/// tracks (for example mp3, aac, ...).
///
/// `TagTunesTrack` should be regarded as a protocol. Due to Swift's limitation
/// with associated types this is currently not possible. Nevertheless every the
/// class `TagTunesTrack` can not be used by itself.
public class TagTunesTrack: NSObject, NSSecureCoding {
/// The entity containing the track.
public internal(set) weak var entity: TagTunesEntityItem?
/// The track's lookup state. By default the state is `Unprocessed`. The
/// state is changed by the lookup controller and may be changed on any
/// thread.
public internal(set) var lookupState = LookupState.Unprocessed
// TODO: Change DOcumentation
/// Returns the `id` of the track. The id can be used to query the track on
/// the iTunes Store. If no such id exists, `nil` is returned.
///
/// This property may block the main thread until the id could be fetched.
/// Subclasses may choose to use the `NSProgress` mechanism to report the
/// progress of the fetching of the id. If possible the id should be cached
/// to minimize waiting time for the user.
///
/// This property must be overridden by subclasses.
public private(set) var id: SearchAPIID?
// TODO: Documentation
public func updateTrackID() {
do {
try id = readTrackID()
} catch {
lookupState = LookupState.Unqualified(error)
}
}
// TODO: Documentation
public func readTrackID() throws -> SearchAPIID {
fatalError("Must override readTrackID()")
}
/// Initializes the track. This is the designated initializer of
/// `TagTunesTrack`. Also it is the only initializer that does not crash the
/// programm.
public override init() {
super.init()
}
/// Decodes the track. This method must be implemented by subclasses.
/// Subclasses must not call this method on `super` in their implementation.
/// Instead subclasses should use `super.init()`.
@objc public required init?(coder aDecoder: NSCoder) {
fatalError("Must override init(coder:)")
}
/// Encoded the track. This method must be implemented by subclasses.
@objc public func encodeWithCoder(aCoder: NSCoder) {
fatalError("Must override encodeWithCoder(_:)")
}
@objc public static func supportsSecureCoding() -> Bool {
return true
}
/// Per-tag querying. This method must be implemented by subclasses. It is
/// generally not a good idea to return `nil` from this method.
public func valueForTag(tag: Tag) -> AnyObject! {
fatalError("Must override valueForTag(_:)")
}
/// Reveals the track. Normally this launches another application in which to
/// reveal the track. Which application is launched depends on the actual
/// track.
///
/// This method must be implemented by subclasses.
public func reveal() {
fatalError("Must override reveal()")
}
// TODO: Documentation
public var supportsBatchSaving: Bool {
fatalError("Must override property supportsBatchSaving")
}
// TODO: Remove
/// Saves the track using the data from the specified `entity`. Subclasses
/// may use the `NSProgress` mechanism to report the saving process.
///
/// This method may be overridden by subclasses. It should respect the user's
/// preferences. The default implementation invokes `saveTag(_:value:)` for
/// each tag to be saved.
///
/// - returns: `true` if the `tags` could be saved successfully, `false`
/// otherwise. If `false` is returned the `saveErrors` property
/// should contain information about the errors that occured. If
/// `true` is returned `saveErrors` should be empty.
// public func save(entity: TagTunesEntityItem) -> Bool {
// var success = true
// saveErrors = []
// let progress = NSProgress(totalUnitCount: Int64(Tag.allTags.count))
// for tag in Tag.allTags {
// do {
// switch Preferences.sharedPreferences.tagSavingBehaviors[tag]! {
// case .Save: try success = success && saveTag(tag, value: entity.valueForTag(tag))
// case .Clear: success = success && saveTag(tag, value: nil)
// case .Ignore: break
// }
// } catch let error {
// saveErrors.append(error)
// }
// dispatch_sync(dispatch_get_main_queue()) {
// progress.completedUnitCount += 1
// }
// }
// return success
// }
// TODO: Documentation
public func saveTags(tags: [Tag: AnyObject?]) throws {
fatalError("Must override saveTags(_:)")
}
// TODO: throws documentation
/// Saves the specified `value` for the specified `tag`. This method should
/// not act depending on the user's `Preferences`.
///
/// This method must be overridden by subclasses.
///
/// - parameters:
/// - value: The value to be saved. If this value is `nil` the value for
/// the specified `tag` should be removed from the track.
/// - tag: The `Tag` to be saved.
public func saveValue(value: AnyObject?, forTag tag: Tag) throws {
fatalError("Must override saveTag(_:value:)")
}
public override var hashValue: Int {
fatalError("Must override property hashValue.")
}
public override func isEqual(object: AnyObject?) -> Bool {
if let other = object as? TagTunesTrack {
return self == other
}
return super.isEqual(object)
}
public override var hash: Int {
return hashValue
}
}
public func ==(lhs: TagTunesTrack, rhs: TagTunesTrack) -> Bool {
if let leftTrack = lhs as? ImportedTrack, rightTrack = rhs as? ImportedTrack {
return leftTrack == rightTrack
}
return lhs === rhs
}
public extension TagTunesTrack {
/// The track's name.
public var name: String? {
return valueForTag(.Name) as? String
}
/// The track's artist.
public var artist: String? {
return valueForTag(.Artist) as? String
}
/// The track's year.
public var year: Int? {
return valueForTag(.Year) as? Int
}
/// The track's track number.
public var trackNumber: Int? {
return valueForTag(.TrackNumber) as? Int
}
/// The track's track count. The track count is respective to the track's
/// `discNumber`.
public var trackCount: Int? {
return valueForTag(.TrackCount) as? Int
}
/// The track's disc number.
public var discNumber: Int? {
return valueForTag(.DiscNumber) as? Int
}
/// The track's disc count.
public var discCount: Int? {
return valueForTag(.DiscCount) as? Int
}
/// The track's genre.
public var genre: String? {
return valueForTag(.Genre) as? String
}
/// The track's album.
public var album: String? {
return valueForTag(.AlbumName) as? String
}
/// The track's album artist.
public var albumArtist: String? {
return valueForTag(.AlbumArtist) as? String
}
/// The track's release date.
public var releaseDate: NSDate? {
return valueForTag(.ReleaseDate) as? NSDate
}
/// A boolean value indicating whether the track belongs to a compilation
/// album.
public var compilation: Bool? {
return valueForTag(.Compilation) as? Bool
}
/// The track's artwork.
public var artwork: NSImage? {
return valueForTag(.Artwork) as? NSImage
}
/// The track's sort name.
public var sortName: String? {
return valueForTag(.SortName) as? String
}
/// The track's sort artist.
public var sortArtist: String? {
return valueForTag(.SortArtist) as? String
}
/// The track's sort album.
public var sortAlbum: String? {
return valueForTag(.SortAlbumName) as? String
}
/// The track's sort album artist.
public var sortAlbumArtist: String? {
return valueForTag(.SortAlbumArtist) as? String
}
/// The track's composer.
public var composer: String? {
return valueForTag(.Composer) as? String
}
/// The track's sort composer.
public var sortComposer: String? {
return valueForTag(.SortComposer) as? String
}
/// The track's comment.
public var comment: String? {
return valueForTag(.Comment) as? String
}
}