--- /dev/null
+image: lyriclitest/swift:5.0.1
+
+stages:
+ - lint
+
+cache:
+ key: ${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHA}
+ paths:
+ - .build/
+
+lint:
+ stage: lint
+ script:
+ - make lint
+++ /dev/null
-language:
- - swift
-
-osx_image: xcode8.3
-
-install:
- - gem install jazzy
- - "./Scripts/install_sourcekitten.sh"
- - "./Scripts/install_swiftlint.sh"
-
-script:
- - make lint
- - make build
-
-before_deploy:
- - make document
-
-deploy:
- provider: pages
- skip_cleanup: true
- github_token: "$GITHUB_TOKEN"
- local_dir: docs
- on:
- branch: master
-
-env:
- global:
- secure: EyOzJFSGY2ifBVqnQz7Xc0sDcg9maLb7VDKWIC2+1n2RsMHGptsxDfJf9r/bOc2kJN9mCzw19eA3XTkypeHKgIgPZ+boLPTDqiiNcD+0iVkYxqw/Q0v5et1+pJaOUo93cKfl2WLWXvISU1MYuzbjGwmnjPDUmujTwGZH1SFvhOKynqx9V/PiL4ZF+CurU2far+diLDhJXUPT4mDV6lDfiALUBvfj50AplM928Vwc6xr71SFii4fE+1GGGGI23ZyXmhnYIJBfQ/9d2wzW6szSRz+q0Gq8jQFJ2cZmBQPnfPY6/xARkDIf5H55HIxLg8pqA7Yn+WDT6/a8uoFLY6OzI8B/TTZ/pX4LXhkK0gbmXeeigRjxN3Dcsb++n9e5+3/Bq0y/Vm+Ufy+TtEvExvU6vdzDu8YZQaE0T2Loyqaw3BQBMoCunv4i7z0crXTLyNYNuc3zDGDmjkR3laxX8lcEZ85zTRTuYqxmvQxkxWUHKYQOvGy7SfkD1xc73f1XvCqpx45utZX0U/OzIxRflWFNy4mlgLvo23h5T0b44LGBBBWEVkjt5YduOuSo9L1wtOrADcDYyxSciIby2SHd4B2fGOb059KyCIUcX/qgOS6FJlmPeC963NCAuZB6DyscaoT6DrJto9nuZW2wNYdo7dvCC2E4ZqHnRPl2zux/RTmeuCU=
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
+## [Unreleased]
+
+## [1.0.0]
+### Added
+- `printLyrics(_ currentTrack: Track)` allows directly specifying
+ track in library
+- `lyricli_command` specifies the flags
+- Support for catalina Music app
+
+### Changed
+- CI scripts updated for gitlab CI instead of Travis
+- Flags are now single words
+- Update to Swift 5 tools
+- Replace CommandLineKit with Bariloche
+- Reorganize sources folder
+- Does not open apps when running
+
+### Removed
+- Arguments Source (functionality moved to main)
+
## [0.3.0]
### Added
- Spotify Source Support
- Parsing of options to match legacy lyricli
- Placeholder for the library with expected endpoints
+[1.0.0]: https://github.com/lyricli-app/lyricli/compare/0.3.0...1.0.0
[0.3.0]: https://github.com/lyricli-app/lyricli/compare/0.2.0...0.3.0
[0.2.0]: https://github.com/lyricli-app/lyricli/compare/0.1.0...0.2.0
[Unreleased]: https://github.com/lyricli-app/lyricli/compare/master...develop
--- /dev/null
+ARG swift_version=latest
+FROM swift:${swift_version}
+
+RUN apt-get update && apt-get install -y \
+ libsqlite3-dev \
+ ruby \
+ ruby-dev \
+ wget \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN gem install --no-ri --no-rdoc jazzy
+
+# SourceKitten
+
+RUN git clone https://github.com/jpsim/SourceKitten.git /tmp/SourceKitten \
+ && cd /tmp/SourceKitten \
+ && make install \
+ && rm -rf /tmp/SourceKitten
+
+# Swiftlint
+
+RUN git clone https://github.com/realm/SwiftLint.git /tmp/SwiftLint \
+ && cd /tmp/SwiftLint \
+ && git submodule update --init --recursive \
+ && make install \
+ && rm -rf /tmp/SwiftLint
install_path = /usr/local/bin
source_binary_path = $(build_path)/$(configuration)/$(source_binary_name)
install_binary_path = $(install_path)/$(target_binary_name)
+swift_version = 5.0.1
# Default to release configuration on install
install: configuration = release
--readme README.md \
--clean \
--author Lyricli \
- --author_url https://github.com/lyricli-app \
- --github_url https://github.com/lyricli-app/lyricli \
- --module-version 0.3.0 \
+ --author_url https://gitlab.com/lyricli \
+ --github_url https://gitlab.com/lyricli/lyricli \
+ --module-version 1.0.0 \
--module Lyricli \
clean:
- swift build --clean
+ swift package clean
-.PHONY: build install test clean lint
+docker-build:
+ docker build --force-rm --build-arg swift_version=$(swift_version) -t lyriclitest/swift:$(swift_version) .
+
+docker-push: docker-build
+ docker push lyriclitest/swift:$(swift_version)
+
+.PHONY: build install test clean lint docker-build docker-push
+++ /dev/null
-{
- "autoPin": true,
- "pins": [
- {
- "package": "CommandeLineKit",
- "reason": null,
- "repositoryURL": "https://github.com/rbdr/CommandLineKit",
- "version": "4.0.0"
- },
- {
- "package": "HTMLEntities",
- "reason": null,
- "repositoryURL": "https://github.com/IBM-Swift/swift-html-entities.git",
- "version": "3.0.3"
- }
- ],
- "version": 1
-}
\ No newline at end of file
--- /dev/null
+{
+ "object": {
+ "pins": [
+ {
+ "package": "Bariloche",
+ "repositoryURL": "https://github.com/Subito-it/Bariloche",
+ "state": {
+ "branch": null,
+ "revision": "507f4121d2a7479522908dabf83aed78a6e5e268",
+ "version": "1.0.4"
+ }
+ },
+ {
+ "package": "Rainbow",
+ "repositoryURL": "https://github.com/onevcat/Rainbow",
+ "state": {
+ "branch": null,
+ "revision": "9c52c1952e9b2305d4507cf473392ac2d7c9b155",
+ "version": "3.1.5"
+ }
+ },
+ {
+ "package": "HTMLEntities",
+ "repositoryURL": "https://github.com/IBM-Swift/swift-html-entities.git",
+ "state": {
+ "branch": null,
+ "revision": "dc15f4d8eba5be23280a561c698fc36ab4fb6c76",
+ "version": "3.0.12"
+ }
+ }
+ ]
+ },
+ "version": 1
+}
+// swift-tools-version:5.0
+
import PackageDescription
let package = Package(
name: "lyricli",
dependencies: [
- .Package(url: "https://github.com/rbdr/CommandLineKit", majorVersion: 4, minor: 0),
- .Package(url: "https://github.com/IBM-Swift/swift-html-entities.git", majorVersion: 3, minor: 0)
+ /// 🔡 Tools for working with HTML entities
+ .package(url: "https://github.com/IBM-Swift/swift-html-entities.git", from: "3.0.11"),
+
+ /// 🚩 Command Line Arguments
+ .package(url: "https://github.com/Subito-it/Bariloche", from: "1.0.4")
+ ],
+ targets: [
+ .target(
+ name: "lyricli",
+ dependencies: ["HTMLEntities", "Bariloche"])
]
)
In order to configure
-* `lrc -l` or `lrc --list-sources` lists the available sources. Enabled
+* `lrc -l` or `lrc --list` lists the available sources. Enabled
sourcess will have a `*`
* `lrc -e` or `lrc --enable <source>` enables a source
* `lrc -d` or `lrc --disable <source>` disables a source
-* `lrc -r` or `lrc --reset-source <source>` resets the configuration for
+* `lrc -r` or `lrc --reset <source>` resets the configuration for
a source and disables it.
* `lrc -v` or `lrc --version` prints the version
* `lrc -h` or `lrc --help` display built-in help
## Building
-The build has only been tested on OSX using Swift 3.1. Building defaults
+The build has only been tested on OSX using Swift 5.0.1. Building defaults
to the debug configuration.
```
make test
```
-[![Build Status](https://travis-ci.org/lyricli-app/lyricli.svg?branch=master)](https://travis-ci.org/lyricli-app/lyricli)
-
[swiftlint]: https://github.com/realm/SwiftLint
[jazzy]: https://github.com/realm/jazzy
[sourcekitten]: https://github.com/jpsim/SourceKitten
+++ /dev/null
-#!/bin/bash
-
-# Taken from: https://alexplescan.com/posts/2016/03/03/setting-up-swiftlint-on-travis-ci/
-# And adapted for sourcekitten
-
-# Installs the SourceKitten package.
-# Tries to get the precompiled .pkg file from Github, but if that
-# fails just recompiles from source.
-
-set -e
-
-SOURCEKITTEN_PKG_PATH="/tmp/SourceKitten.pkg"
-SOURCEKITTEN_PKG_URL="https://github.com/jpsim/SourceKitten/releases/download/0.17.3/SourceKitten.pkg"
-
-wget --output-document=$SOURCEKITTEN_PKG_PATH $SOURCEKITTEN_PKG_URL
-
-if [ -f $SOURCEKITTEN_PKG_PATH ]; then
- echo "SourceKitten package exists! Installing it..."
- sudo installer -pkg $SOURCEKITTEN_PKG_PATH -target /
-else
- echo "SourceKitten package doesn't exist. Compiling from source..." &&
- git clone https://github.com/jspim/SourceKitten.git /tmp/SourceKitten &&
- cd /tmp/SourceKitten &&
- sudo make install
-fi
+++ /dev/null
-#!/bin/bash
-
-# Taken from: https://alexplescan.com/posts/2016/03/03/setting-up-swiftlint-on-travis-ci/
-
-# Installs the SwiftLint package.
-# Tries to get the precompiled .pkg file from Github, but if that
-# fails just recompiles from source.
-
-set -e
-
-SWIFTLINT_PKG_PATH="/tmp/SwiftLint.pkg"
-SWIFTLINT_PKG_URL="https://github.com/realm/SwiftLint/releases/download/0.18.1/SwiftLint.pkg"
-
-wget --output-document=$SWIFTLINT_PKG_PATH $SWIFTLINT_PKG_URL
-
-if [ -f $SWIFTLINT_PKG_PATH ]; then
- echo "SwiftLint package exists! Installing it..."
- sudo installer -pkg $SWIFTLINT_PKG_PATH -target /
-else
- echo "SwiftLint package doesn't exist. Compiling from source..." &&
- git clone https://github.com/realm/SwiftLint.git /tmp/SwiftLint &&
- cd /tmp/SwiftLint &&
- git submodule update --init --recursive &&
- sudo make install
-fi
+++ /dev/null
-// Source that reads track artist and name from the command line
-class ArgumentsSource: Source {
-
- // Returns a track based on the arguments. It assumes the track artist
- // will be the first argument, and the name will be the second, excluding
- // any flags.
- var currentTrack: Track? {
-
- if CommandLine.arguments.count >= 3 {
- // expected usage: $ ./lyricli <artist> <name>
- let trackName: String = CommandLine.arguments[2]
- let trackArtist: String = CommandLine.arguments[1]
-
- return Track(withName: trackName, andArtist: trackArtist)
- }
- return nil
- }
-}
// Default options, will be automatically written to the global config if
// not found.
private var configuration: [String: Any] = [
- "enabled_sources": ["arguments", "itunes", "spotify"]
+ "enabled_sources": ["itunes", "spotify"]
]
// The shared instance of the object
class Lyricli {
// Version of the application
- static var version = "0.3.0"
+ static var version = "1.0.0"
// Flag that controls whether we should show the track artist and name before
// the lyrics
let sourceManager = SourceManager()
if let currentTrack = sourceManager.currentTrack {
- let engine = LyricsEngine(withTrack: currentTrack)
+ printLyrics(currentTrack)
+ } else {
+ print("No Artist/Song could be found :(")
+ }
+ }
- if let lyrics = engine.lyrics {
- if showTitle {
- printTitle(currentTrack)
- }
+ // fetches the lyrics from an engine and prints them
+ static func printLyrics(_ currentTrack: Track) {
+ let engine = LyricsEngine(withTrack: currentTrack)
- print(lyrics)
- } else {
- print("Lyrics not found :(")
+ if let lyrics = engine.lyrics {
+ if showTitle {
+ printTitle(currentTrack)
}
+ print(lyrics)
} else {
- print("No Artist/Song could be found :(")
+ print("Lyrics not found :(")
}
}
--- /dev/null
+import Bariloche
+
+class LyricliCommand: Command {
+ let usage: String? = "Fetch the lyrics for current playing track or the one specified via arguments"
+
+ // 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")
+
+ // 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")
+
+ // 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")
+
+ func run() -> Bool {
+ return true
+ }
+}
// Look for the lyrics lightbox
if let regex = try? NSRegularExpression(pattern: lyricsMatcher) {
- let matches = regex.matches(in: body, range: NSRange(location: 0, length: body.characters.count))
+ let matches = regex.matches(in: body, range: NSRange(location: 0, length: body.count))
for match in matches {
let nsBody = body as NSString
- let range = match.rangeAt(1)
+ let range = match.range(at: 1)
let encodedLyrics = nsBody.substring(with: range)
let decodedLyrics = decodeLyrics(encodedLyrics)
--- /dev/null
+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()
// List of sources enabled for the crurent platform
private var availableSources: [String: Source] = [
- "arguments": ArgumentsSource(),
"itunes": ItunesSource(),
"spotify": SpotifySource()
]
import ScriptingBridge
+import Foundation
// Protocol to obtain the track from iTunes
@objc protocol iTunesTrack {
@objc optional var currentStreamTitle: String? {get}
}
-extension SBApplication : iTunesApplication {}
+extension SBApplication: iTunesApplication {}
// Source that reads track artist and name from current itunes track
class ItunesSource: Source {
// Calls the spotify API and returns the current track
var currentTrack: Track? {
- if let iTunes: iTunesApplication = SBApplication(bundleIdentifier: "com.apple.iTunes") {
+ if let iTunes: iTunesApplication = SBApplication(bundleIdentifier: bundleIdentifier) {
+ if let application = iTunes as? SBApplication {
+ if !application.isRunning {
+ return nil
+ }
+ }
// Attempt to fetch the title from a stream
if let currentStreamTitle = iTunes.currentStreamTitle {
if let track = currentStreamTitle {
- let trackComponents = track.characters.split(separator: "-").map(String.init)
+ let trackComponents = track.split(separator: "-").map(String.init)
if trackComponents.count == 2 {
let artist = trackComponents[0].trimmingCharacters(in: .whitespaces)
if let artist = track.artist {
// track properties are empty strings if itunes is closed
- if (!(name != "" && artist != "")) {
+ if name == "" || artist == "" {
return nil
}
return Track(withName: name, andArtist: artist)
return nil
}
+ private var bundleIdentifier: String {
+ if ProcessInfo().isOperatingSystemAtLeast(
+ OperatingSystemVersion(majorVersion: 10, minorVersion: 15, patchVersion: 0)
+ ) {
+ return "com.apple.Music"
+ }
+
+ return "com.apple.iTunes"
+ }
+
}
@objc optional var currentTrack: SpotifyTrack? {get}
}
-extension SBApplication : SpotifyApplication {}
+extension SBApplication: SpotifyApplication {}
// Source that reads track artist and name from current Spotify track
class SpotifySource: Source {
var currentTrack: Track? {
if let spotify: SpotifyApplication = SBApplication(bundleIdentifier: "com.spotify.client") {
+ if let application = spotify as? SBApplication {
+ if !application.isRunning {
+ return nil
+ }
+ }
// Attempt to fetch the title from a song
+
if let currentTrack = spotify.currentTrack {
if let track = currentTrack {
if let name = track.name {
}
}
-
+++ /dev/null
-import CommandLineKit
-import Foundation
-
-// Entry point of the application. This is the main executable
-private func main() {
- let (flags, parser) = createParser()
-
- do {
- try parser.parse()
- } catch {
- parser.printUsage(error)
- exit(EX_USAGE)
- }
-
- // Boolean Options
-
- checkHelpFlag(flags["help"], withParser: parser)
- checkVersionFlag(flags["version"], withParser: parser)
- checkListSourcesFlag(flags["listSources"], withParser: parser)
- checkTitleFlag(flags["title"], withParser: parser)
-
- // String Options
-
- checkEnableSourceFlag(flags["enableSource"], withParser: parser)
- checkDisableSourceFlag(flags["disableSource"], withParser: parser)
- checkResetSourceFlag(flags["resetSource"], withParser: parser)
-
- // Remove any flags so anyone after this gets the unprocessed values
-
- let programName: [String] = [CommandLine.arguments[0]]
- CommandLine.arguments = programName + parser.unparsedArguments
-
- // Run Lyricli
-
- Lyricli.printLyrics()
-}
-
-/// Sets up and returns a new options parser
-///
-/// - Returns: A Dictionary of Options, and a new CommandLineKit instance
-private func createParser() -> ([String:Option], CommandLineKit) {
- let parser = CommandLineKit()
- var flags: [String:Option] = [:]
-
- flags["help"] = BoolOption(shortFlag: "h", longFlag: "help", helpMessage: "Prints a help message.")
- flags["version"] = BoolOption(shortFlag: "v", longFlag: "version", helpMessage: "Prints the version.")
-
- flags["enableSource"] = StringOption(shortFlag: "e", longFlag: "enable-source", helpMessage: "Enables a source")
- flags["disableSource"] = StringOption(shortFlag: "d", longFlag: "disable-source", helpMessage: "Disables a source")
- flags["resetSource"] = StringOption(shortFlag: "r", longFlag: "reset-source", helpMessage: "Resets a source")
- flags["listSources"] = BoolOption(shortFlag: "l", longFlag: "list-sources", helpMessage: "Lists all sources")
-
- flags["title"] = BoolOption(shortFlag: "t", longFlag: "title", helpMessage: "Shows title of song if true")
-
- parser.addOptions(Array(flags.values))
-
- parser.formatOutput = {parseString, type in
-
- var formattedString: String
-
- switch type {
- case .About:
- formattedString = "\(parseString) [<artist_name> <song_name>]"
- break
- default:
- formattedString = parseString
- }
-
- return parser.defaultFormat(formattedString, type: type)
- }
-
- return (flags, parser)
-}
-
-// Handle the Help flag
-
-private func checkHelpFlag(_ flag: Option?, withParser parser: CommandLineKit) {
- if let helpFlag = flag as? BoolOption {
- if helpFlag.value {
- parser.printUsage()
- exit(0)
- }
- }
-}
-
-// Handle the version flag
-
-private func checkVersionFlag(_ flag: Option?, withParser parser: CommandLineKit) {
- if let versionFlag = flag as? BoolOption {
- if versionFlag.value {
- print(Lyricli.version)
- exit(0)
- }
- }
-}
-
-// Handle the list sources flag
-
-private func checkListSourcesFlag(_ flag: Option?, withParser parser: CommandLineKit) {
- if let listSourcesFlag = flag as? BoolOption {
- if listSourcesFlag.value {
- Lyricli.printSources()
- exit(0)
- }
- }
-}
-
-// Handle the title flag
-
-private func checkTitleFlag(_ flag: Option?, withParser parser: CommandLineKit) {
- if let titleFlag = flag as? BoolOption {
- if titleFlag.value {
- Lyricli.showTitle = true
- }
- }
-}
-
-// Handle the enable source flag
-
-private func checkEnableSourceFlag(_ flag: Option?, withParser parser: CommandLineKit) {
- if let enableSourceFlag = flag as? StringOption {
- if let source = enableSourceFlag.value {
- Lyricli.enableSource(source)
- exit(0)
- }
- }
-}
-
-// Handle the disable source flag
-
-private func checkDisableSourceFlag(_ flag: Option?, withParser parser: CommandLineKit) {
- if let disableSourceFlag = flag as? StringOption {
- if let source = disableSourceFlag.value {
- Lyricli.disableSource(source)
- exit(0)
- }
- }
-}
-
-// Handle the reset source flag
-
-private func checkResetSourceFlag(_ flag: Option?, withParser parser: CommandLineKit) {
- if let resetSourceFlag = flag as? StringOption {
- if let source = resetSourceFlag.value {
- Lyricli.resetSource(source)
- exit(0)
- }
- }
-}
-
-main()