let package = Package(
name: "lyricli",
dependencies: [
+ /// HTML Parsing
+ .package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.5.3"),
+
/// 🚩 Command Line Arguments
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2")
],
targets: [
.executableTarget(
name: "lyricli",
- dependencies: [.product(name: "ArgumentParser", package: "swift-argument-parser")])
+ dependencies: [
+ .product(name: "SwiftSoup", package: "SwiftSoup"),
+ .product(name: "ArgumentParser", package: "swift-argument-parser")
+ ])
]
)
import Foundation
+import SwiftSoup
// Given a track, attempts to fetch the lyrics from lyricswiki
class LyricsEngine {
// Method used to call the API
private let apiMethod = "GET"
- // Regular expxression used to find the lyrics in the lyricswiki HTML
- private let lyricsMatcher = "class='lyricbox'>(.+)<div"
-
// The track we'll be looking for
private let track: Track
// Encode the track artist and name and finish building the API call URL
if let artist = track.artist.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
- if let name: String = track.name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
- if let url = URL(string: "\(apiURL)&q=\(artist) \(name)") {
+ if let name = track.name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
+
+ if let url = URL(string: "\(apiURL)?access_token=\(clientToken)&q=\(artist)%20\(name)") {
// We'll lock until the async call is finished
if let data = data {
if let jsonResponse = try? JSONSerialization.jsonObject(with: data) {
if let jsonResponse = jsonResponse as? [String: Any] {
- if let hits = jsonResponse["hits"] as? [Any] {
- if let firstHit = hits[0] as? [String: Any] {
- if let firstHitData = firstHit["result"] as? [String: Any] {
- if let lyricsUrlString = firstHitData["url"] as? String {
- if let lyricsUrl = URL(string: lyricsUrlString) {
-
- // At this point we have a valid wiki url
- self.fetchLyricsFromPage(
- withURL: lyricsUrl,
- completionHandler: completionHandler
- )
- return
+ if let response = jsonResponse["response"] as? [String:Any] {
+ if let hits = response["hits"] as? [Any] {
+ if let firstHit = hits[0] as? [String: Any] {
+ if let firstHitData = firstHit["result"] as? [String: Any] {
+ if let lyricsUrlString = firstHitData["url"] as? String {
+ if let lyricsUrl = URL(string: lyricsUrlString) {
+
+ // At this point we have a valid wiki url
+ self.fetchLyricsFromPage(
+ withURL: lyricsUrl,
+ completionHandler: completionHandler
+ )
+ return
+ }
}
}
}
// Parses the wiki to find the lyrics, decodes the lyrics object
private func parseHtmlBody(_ body: String, completionHandler: @escaping (String?) -> Void) {
- // Look for the lyrics lightbox
-
- if let regex = try? NSRegularExpression(pattern: lyricsMatcher) {
- 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.range(at: 1)
- let encodedLyrics = nsBody.substring(with: range)
-
- let decodedLyrics = decodeLyrics(encodedLyrics)
-
- completionHandler(decodedLyrics)
- return
- }
+ do {
+ let document: Document = try SwiftSoup.parse(body)
+ let lyricsBox = try document.select("div[data-lyrics-container=\"true\"]")
+ try lyricsBox.select("br").after("\\n")
+ let lyrics = try lyricsBox.text()
+ completionHandler(lyrics.replacingOccurrences(of: "\\n", with: "\r\n"))
+ } catch {
+ completionHandler(nil)
}
-
- completionHandler(nil)
- }
-
- // Escapes the HTML entities and HTML
- private func decodeLyrics(_ lyrics: String) -> String {
-
- return lyrics
}
}