aboutsummaryrefslogtreecommitdiff
path: root/Sources
diff options
context:
space:
mode:
authorRuben Beltran del Rio <ruben@unlimited.pizza>2023-04-10 13:02:51 +0200
committerRuben Beltran del Rio <ruben@unlimited.pizza>2023-04-10 13:02:51 +0200
commitc53df649c817f770ae1750bb6d11113e0bcd2b18 (patch)
tree6c789c51ba15ba4faa6346b60996bc23a261a6ff /Sources
parent1b8bdf0f82d6273dc39a67ac579d34d5b0d7d21f (diff)
Update code, add source management config
Diffstat (limited to 'Sources')
-rw-r--r--Sources/lyricli/configuration.swift2
-rw-r--r--Sources/lyricli/errors/configuration_could_not_be_read.swift3
-rw-r--r--Sources/lyricli/errors/source_could_not_be_disabled.swift6
-rw-r--r--Sources/lyricli/errors/source_could_not_be_enabled.swift6
-rw-r--r--Sources/lyricli/errors/source_could_not_be_reset.swift6
-rw-r--r--Sources/lyricli/errors/source_not_available.swift3
-rw-r--r--Sources/lyricli/lyricli.swift60
-rw-r--r--Sources/lyricli/lyricli_command.swift115
-rw-r--r--Sources/lyricli/lyrics_engine.swift11
-rw-r--r--Sources/lyricli/main.swift107
-rw-r--r--Sources/lyricli/source_manager.swift4
-rw-r--r--Sources/lyricli/sources/apple_music_source.swift (renamed from Sources/lyricli/sources/itunes_source.swift)25
-rw-r--r--Sources/lyricli/sources/source_protocol.swift4
-rw-r--r--Sources/lyricli/sources/spotify_source.swift3
14 files changed, 189 insertions, 166 deletions
diff --git a/Sources/lyricli/configuration.swift b/Sources/lyricli/configuration.swift
index 1b01034..b2defc1 100644
--- a/Sources/lyricli/configuration.swift
+++ b/Sources/lyricli/configuration.swift
@@ -8,7 +8,7 @@ class Configuration {
// Default options, will be automatically written to the global config if
// not found.
private var configuration: [String: Any] = [
- "enabled_sources": ["itunes", "spotify"]
+ "enabled_sources": ["apple_music", "spotify"]
]
// The shared instance of the object
diff --git a/Sources/lyricli/errors/configuration_could_not_be_read.swift b/Sources/lyricli/errors/configuration_could_not_be_read.swift
new file mode 100644
index 0000000..fb2b61a
--- /dev/null
+++ b/Sources/lyricli/errors/configuration_could_not_be_read.swift
@@ -0,0 +1,3 @@
+struct ConfigurationCouldNotBeRead: Error {
+ var localizedDescription = "The configuration could not be read, check ~/.lyricli.conf"
+}
diff --git a/Sources/lyricli/errors/source_could_not_be_disabled.swift b/Sources/lyricli/errors/source_could_not_be_disabled.swift
new file mode 100644
index 0000000..b19f3c5
--- /dev/null
+++ b/Sources/lyricli/errors/source_could_not_be_disabled.swift
@@ -0,0 +1,6 @@
+// Future improvement: At the moment the sources don't need any special
+// configuration. Once we do have more operations it would make sense
+// to throw more descriptive errors.
+struct SourceCouldNotBeDisabled: Error {
+ var localizedDescription = "The selected source failed while disabling"
+}
diff --git a/Sources/lyricli/errors/source_could_not_be_enabled.swift b/Sources/lyricli/errors/source_could_not_be_enabled.swift
new file mode 100644
index 0000000..7797478
--- /dev/null
+++ b/Sources/lyricli/errors/source_could_not_be_enabled.swift
@@ -0,0 +1,6 @@
+// Future improvement: At the moment the sources don't need any special
+// configuration. Once we do have more operations it would make sense
+// to throw more descriptive errors.
+struct SourceCouldNotBeEnabled: Error {
+ var localizedDescription = "The selected source failed while enabling"
+}
diff --git a/Sources/lyricli/errors/source_could_not_be_reset.swift b/Sources/lyricli/errors/source_could_not_be_reset.swift
new file mode 100644
index 0000000..beecf54
--- /dev/null
+++ b/Sources/lyricli/errors/source_could_not_be_reset.swift
@@ -0,0 +1,6 @@
+// Future improvement: At the moment the sources don't need any special
+// configuration. Once we do have more operations it would make sense
+// to throw more descriptive errors.
+struct SourceCouldNotBeReset: Error {
+ var localizedDescription = "The selected source failed while resetting"
+}
diff --git a/Sources/lyricli/errors/source_not_available.swift b/Sources/lyricli/errors/source_not_available.swift
new file mode 100644
index 0000000..e3d598d
--- /dev/null
+++ b/Sources/lyricli/errors/source_not_available.swift
@@ -0,0 +1,3 @@
+struct SourceNotAvailable: Error {
+ var localizedDescription = "The selected source wasn't available"
+}
diff --git a/Sources/lyricli/lyricli.swift b/Sources/lyricli/lyricli.swift
index 043c004..ad4bb86 100644
--- a/Sources/lyricli/lyricli.swift
+++ b/Sources/lyricli/lyricli.swift
@@ -25,11 +25,10 @@ class Lyricli {
static func printLyrics(_ currentTrack: Track) {
let engine = LyricsEngine(withTrack: currentTrack)
+ if showTitle {
+ printTitle(currentTrack)
+ }
if let lyrics = engine.lyrics {
- if showTitle {
- printTitle(currentTrack)
- }
-
print(lyrics)
} else {
print("Lyrics not found :(")
@@ -38,23 +37,62 @@ class Lyricli {
// Print the currently available sources
static func printSources() {
- print("Listing Sources: Not yet implemented")
+ let sourceManager = SourceManager()
+ for (sourceName, _) in sourceManager.availableSources {
+ if (Configuration.shared["enabled_sources"] as? [String] ?? []).contains(sourceName) {
+ print("\(sourceName) (enabled)")
+ } else {
+ print(sourceName)
+ }
+ }
}
// Runs the enable method of a source and writes the configuration to set it
// as enabled
- static func enableSource(_ sourceName: String) {
- print("Enable source \(sourceName): Not yet implemented")
+ static func enableSource(_ sourceName: String) throws {
+ let sourceManager = SourceManager()
+ if let source = sourceManager.availableSources[sourceName] {
+ if let enabledSources = Configuration.shared["enabled_sources"] as? [String] {
+ if source.enable() == false {
+ throw SourceCouldNotBeEnabled()
+ }
+ if !enabledSources.contains(sourceName) {
+ Configuration.shared["enabled_sources"] = enabledSources + [sourceName]
+ }
+ return
+ }
+ throw ConfigurationCouldNotBeRead()
+ }
+ throw SourceNotAvailable()
}
// Remove a source from the enabled sources configuration
- static func disableSource(_ sourceName: String) {
- print("Disable source \(sourceName): Not yet implemented")
+ static func disableSource(_ sourceName: String) throws {
+ let sourceManager = SourceManager()
+ if let source = sourceManager.availableSources[sourceName] {
+ if let enabledSources = Configuration.shared["enabled_sources"] as? [String] {
+ if source.disable() == false {
+ throw SourceCouldNotBeDisabled()
+ }
+ Configuration.shared["enabled_sources"] = enabledSources.filter { $0 != sourceName }
+ return
+ }
+ throw ConfigurationCouldNotBeRead()
+ }
+ throw SourceNotAvailable()
}
// Removes any configuration for a source, and disables it
- static func resetSource(_ sourceName: String) {
- print("Reset source \(sourceName): Not yet implemented")
+ static func resetSource(_ sourceName: String) throws {
+ let sourceManager = SourceManager()
+ if let source = sourceManager.availableSources[sourceName] {
+ if source.reset() == false {
+ throw SourceCouldNotBeReset()
+ }
+ try disableSource(sourceName)
+ return
+ }
+ throw SourceNotAvailable()
}
// Prints the track artist and name
diff --git a/Sources/lyricli/lyricli_command.swift b/Sources/lyricli/lyricli_command.swift
index 88f9c7b..872ab6e 100644
--- a/Sources/lyricli/lyricli_command.swift
+++ b/Sources/lyricli/lyricli_command.swift
@@ -1,38 +1,95 @@
-import Bariloche
+import Darwin
+import ArgumentParser
-class LyricliCommand: Command {
- let usage: String? = "Fetch the lyrics for current playing track or the one specified via arguments"
+@main
+struct LyricliCommand: ParsableCommand {
+
+ // Positional Arguments
+ @Argument var artist: String?
+ @Argument var trackName: String?
// Flags
- let version = Flag(short: "v", long: "version", help: "Prints the version.")
- let showTitle = Flag(short: "t", long: "title", help: "Shows title of song if true")
- let listSources = Flag(short: "l", long: "list", help: "Lists all sources")
+ @Flag(name: .shortAndLong, help: "Prints the version.")
+ var version = false
+
+ @Flag(name: [.long, .customShort("t")], help: "Shows title of track if true")
+ var showTitle = false
+
+ @Flag(name: .shortAndLong, help: "Lists all sources")
+ var listSources = false
// Named Arguments
- let enableSource = Argument<String>(name: "source",
- kind: .named(short: "e", long: "enable"),
- optional: true,
- help: "Enables a source")
- let disableSource = Argument<String>(name: "source",
- kind: .named(short: "d", long: "disable"),
- optional: true,
- help: "Disables a source")
- let resetSource = Argument<String>(name: "source",
- kind: .named(short: "r", long: "reset"),
- optional: true,
- help: "Resets a source")
+ @Option(name: .shortAndLong, help: ArgumentHelp("Enables a source", valueName: "source"))
+ var enableSource: String?
- // Positional Arguments
- let artist = Argument<String>(name: "artist",
- kind: .positional,
- optional: true,
- help: "The name of the artist")
- let trackName = Argument<String>(name: "trackName",
- kind: .positional,
- optional: true,
- help: "The name of the track")
+ @Option(name: .shortAndLong, help: ArgumentHelp("Disables a source", valueName: "source"))
+ var disableSource: String?
+
+ @Option(name: .shortAndLong, help: ArgumentHelp("Resets a source", valueName: "source"))
+ var resetSource: String?
+
+ mutating func run() throws {
+
+ // Handle the version flag
+ if version {
+ print(Lyricli.version)
+ Darwin.exit(0)
+ }
+
+ // Handle the list sources flag
+ if listSources {
+ Lyricli.printSources()
+ Darwin.exit(0)
+ }
+
+ // Handle the enable source option
+ if let source = enableSource {
+ do {
+ try Lyricli.enableSource(source)
+ } catch let error {
+ handleErrorAndQuit(error)
+ }
+ Darwin.exit(0)
+ }
+
+ // Handle the disable source option
+ if let source = disableSource {
+ do {
+ try Lyricli.disableSource(source)
+ } catch let error {
+ handleErrorAndQuit(error)
+ }
+ Darwin.exit(0)
+ }
+
+ // Handle the reset source flag
+ if let source = resetSource {
+ do {
+ try Lyricli.resetSource(source)
+ } catch let error {
+ handleErrorAndQuit(error)
+ }
+ Darwin.exit(0)
+ }
+
+ Lyricli.showTitle = showTitle
+
+ if let artist {
+ let currentTrack: Track
+ if let trackName {
+ currentTrack = Track(withName: trackName, andArtist: artist)
+ } else {
+ currentTrack = Track(withName: "", andArtist: artist)
+ }
+ Lyricli.printLyrics(currentTrack)
+ Darwin.exit(0)
+ }
+
+ Lyricli.printLyrics()
+ }
- func run() -> Bool {
- return true
+ private func handleErrorAndQuit(_ error: Error) {
+ fputs(error.localizedDescription, stderr)
+ Darwin.exit(1)
}
}
diff --git a/Sources/lyricli/lyrics_engine.swift b/Sources/lyricli/lyrics_engine.swift
index 1a182d0..9ca0cad 100644
--- a/Sources/lyricli/lyrics_engine.swift
+++ b/Sources/lyricli/lyrics_engine.swift
@@ -1,5 +1,4 @@
import Foundation
-import HTMLEntities
// Given a track, attempts to fetch the lyrics from lyricswiki
class LyricsEngine {
@@ -82,7 +81,10 @@ class LyricsEngine {
if let lyricsUrl = URL(string: lyricsUrlString) {
// At this point we have a valid wiki url
- self.fetchLyricsFromPage(withURL: lyricsUrl, completionHandler: completionHandler)
+ self.fetchLyricsFromPage(
+ withURL: lyricsUrl,
+ completionHandler: completionHandler
+ )
return
}
}
@@ -145,10 +147,9 @@ class LyricsEngine {
completionHandler(nil)
}
- // Escapes the HTML entities
+ // Escapes the HTML entities and HTML
private func decodeLyrics(_ lyrics: String) -> String {
- let unescapedLyrics = lyrics.htmlUnescape()
- return unescapedLyrics.replacingOccurrences(of: "<br />", with: "\n")
+ return lyrics
}
}
diff --git a/Sources/lyricli/main.swift b/Sources/lyricli/main.swift
deleted file mode 100644
index be1c933..0000000
--- a/Sources/lyricli/main.swift
+++ /dev/null
@@ -1,107 +0,0 @@
-import Foundation
-import Bariloche
-
-// Entry point of the application. This is the main executable
-private func main() {
-
- // Bariloche assumes at least one argument, so bypass
- // if that's the case.
- if CommandLine.arguments.count > 1 {
- let parser = Bariloche(command: LyricliCommand())
- let result = parser.parse()
-
- if result.count == 0 {
- exit(EX_USAGE)
- }
-
- if let lyricliCommand = result[0] as? LyricliCommand {
- // Flags
- checkVersionFlag(lyricliCommand)
- checkListSourcesFlag(lyricliCommand)
- checkTitleFlag(lyricliCommand)
-
- // String Options
-
- checkEnableSourceFlag(lyricliCommand)
- checkDisableSourceFlag(lyricliCommand)
- checkResetSourceFlag(lyricliCommand)
-
- checkPositionalArguments(lyricliCommand)
-
- }
- }
-
- // Run Lyricli
- Lyricli.printLyrics()
-}
-
-// Handle the version flag
-
-private func checkVersionFlag(_ command: LyricliCommand) {
- if command.version.value {
- print(Lyricli.version)
- exit(0)
- }
-}
-
-// Handle the list sources flag
-
-private func checkListSourcesFlag(_ command: LyricliCommand) {
- if command.listSources.value {
- Lyricli.printSources()
- exit(0)
- }
-}
-
-// Handle the title flag
-
-private func checkTitleFlag(_ command: LyricliCommand) {
- Lyricli.showTitle = command.showTitle.value
-}
-
-// Handle the enable source flag
-
-private func checkEnableSourceFlag(_ command: LyricliCommand) {
- if let source = command.enableSource.value {
- Lyricli.enableSource(source)
- exit(0)
- }
-}
-
-// Handle the disable source flag
-
-private func checkDisableSourceFlag(_ command: LyricliCommand) {
- if let source = command.disableSource.value {
- Lyricli.disableSource(source)
- exit(0)
- }
-}
-
-// Handle the reset source flag
-
-private func checkResetSourceFlag(_ command: LyricliCommand) {
- if let source = command.resetSource.value {
- Lyricli.resetSource(source)
- exit(0)
- }
-}
-
-// Handle the positional arguments
-
-private func checkPositionalArguments(_ command: LyricliCommand) {
- if let artist = command.artist.value {
-
- let currentTrack: Track
-
- if let trackName = command.trackName.value {
- currentTrack = Track(withName: trackName, andArtist: artist)
- } else {
- currentTrack = Track(withName: "", andArtist: artist)
- }
-
- Lyricli.printLyrics(currentTrack)
- exit(0)
- }
-}
-
-main()
diff --git a/Sources/lyricli/source_manager.swift b/Sources/lyricli/source_manager.swift
index 2f0b8f4..481d327 100644
--- a/Sources/lyricli/source_manager.swift
+++ b/Sources/lyricli/source_manager.swift
@@ -2,8 +2,8 @@
class SourceManager {
// List of sources enabled for the crurent platform
- private var availableSources: [String: Source] = [
- "itunes": ItunesSource(),
+ var availableSources: [String: Source] = [
+ "apple_music": AppleMusicSource(),
"spotify": SpotifySource()
]
diff --git a/Sources/lyricli/sources/itunes_source.swift b/Sources/lyricli/sources/apple_music_source.swift
index 92b9969..5eee588 100644
--- a/Sources/lyricli/sources/itunes_source.swift
+++ b/Sources/lyricli/sources/apple_music_source.swift
@@ -1,35 +1,35 @@
import ScriptingBridge
import Foundation
-// Protocol to obtain the track from iTunes
-@objc protocol iTunesTrack {
+// Protocol to obtain the track from
+@objc protocol AppleMusicTrack {
@objc optional var name: String {get}
@objc optional var artist: String {get}
}
-// Protocol to interact with iTunes
-@objc protocol iTunesApplication {
- @objc optional var currentTrack: iTunesTrack? {get}
+// Protocol to interact with Apple Music
+@objc protocol AppleMusicApplication {
+ @objc optional var currentTrack: AppleMusicTrack? {get}
@objc optional var currentStreamTitle: String? {get}
}
-extension SBApplication: iTunesApplication {}
+extension SBApplication: AppleMusicApplication {}
// Source that reads track artist and name from current itunes track
-class ItunesSource: Source {
+class AppleMusicSource: Source {
// Calls the spotify API and returns the current track
var currentTrack: Track? {
- if let iTunes: iTunesApplication = SBApplication(bundleIdentifier: bundleIdentifier) {
- if let application = iTunes as? SBApplication {
+ if let appleMusic: AppleMusicApplication = SBApplication(bundleIdentifier: bundleIdentifier) {
+ if let application = appleMusic as? SBApplication {
if !application.isRunning {
return nil
}
}
// Attempt to fetch the title from a stream
- if let currentStreamTitle = iTunes.currentStreamTitle {
+ if let currentStreamTitle = appleMusic.currentStreamTitle {
if let track = currentStreamTitle {
let trackComponents = track.split(separator: "-").map(String.init)
@@ -45,7 +45,7 @@ class ItunesSource: Source {
}
// Attempt to fetch the title from a song
- if let currentTrack = iTunes.currentTrack {
+ if let currentTrack = appleMusic.currentTrack {
if let track = currentTrack {
if let name = track.name {
if let artist = track.artist {
@@ -74,4 +74,7 @@ class ItunesSource: Source {
return "com.apple.iTunes"
}
+ func enable() -> Bool { return true }
+ func disable() -> Bool { return true }
+ func reset() -> Bool { return true }
}
diff --git a/Sources/lyricli/sources/source_protocol.swift b/Sources/lyricli/sources/source_protocol.swift
index 0885994..8d52603 100644
--- a/Sources/lyricli/sources/source_protocol.swift
+++ b/Sources/lyricli/sources/source_protocol.swift
@@ -2,4 +2,8 @@
// property will return a track if the conditions are met
protocol Source {
var currentTrack: Track? { get }
+
+ func disable() -> Bool
+ func enable() -> Bool
+ func reset() -> Bool
}
diff --git a/Sources/lyricli/sources/spotify_source.swift b/Sources/lyricli/sources/spotify_source.swift
index 9c516c4..863a399 100644
--- a/Sources/lyricli/sources/spotify_source.swift
+++ b/Sources/lyricli/sources/spotify_source.swift
@@ -43,4 +43,7 @@ class SpotifySource: Source {
return nil
}
+ func enable() -> Bool { return true }
+ func disable() -> Bool { return true }
+ func reset() -> Bool { return true }
}