Skip to content

Instantly share code, notes, and snippets.

@eyeplum
Last active August 29, 2015 14:13
Show Gist options
  • Save eyeplum/bf577e19ae3bb86f85b0 to your computer and use it in GitHub Desktop.
Save eyeplum/bf577e19ae3bb86f85b0 to your computer and use it in GitHub Desktop.
RyCooder - Command Line Music Player written in Swift
import Foundation
import AVFoundation
// AudioPlayer Class Definition
class AudioPlayer: NSObject {
private let runLoop: NSRunLoop = NSRunLoop.currentRunLoop()
private let stdinHandle: NSFileHandle = NSFileHandle.fileHandleWithStandardInput()
private let items: [AVPlayerItem]!
private var queuePlayer: AVQueuePlayer!
init(contentPath: String) {
super.init()
let musicFiles = musicFilesInDirectory(contentPath)
logMusicFiles(musicFiles)
items = preparePlayerItemsWithPaths(musicFiles)
queuePlayer = AVQueuePlayer.queuePlayerWithItems(items) as AVQueuePlayer
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
private func handleInput() {
if let inputString = NSString(data: stdinHandle.availableData, encoding: NSUTF8StringEncoding) {
var input = inputString.stringByReplacingOccurrencesOfString("\n", withString:"")
var handledAsNumber = false
if let inputNumber = input.toInt() {
if inputNumber > 0 && inputNumber <= items.count {
jumpToItemAtIndex(inputNumber - 1)
queuePlayer.play()
handledAsNumber = true
}
}
if handledAsNumber == false {
switch input {
case "start":
logStart()
queuePlayer.play()
case "next":
queuePlayer.advanceToNextItem()
queuePlayer.play()
case "previous":
jumpToPreviousItem()
queuePlayer.play()
default:
println("Unrecognized selector \"\(input)\" 😦 .")
}
}
}
stdinHandle.waitForDataInBackgroundAndNotify()
logInputTips()
}
func start() {
handleInput()
NSNotificationCenter.defaultCenter().addObserverForName(
NSFileHandleDataAvailableNotification,
object: nil,
queue: NSOperationQueue.mainQueue()) {
(notification) in
self.handleInput()
}
runLoop.run()
}
private func jumpToItemAtIndex(index: Int) {
queuePlayer.removeAllItems()
for (_, item) in enumerate(items) {
item.seekToTime(kCMTimeZero)
queuePlayer.insertItem(item, afterItem: nil)
}
var i = 0
while i < index {
queuePlayer.advanceToNextItem()
i++
}
}
private func jumpToPreviousItem() {
}
private func logMusicFiles(musicFiles: [String]) {
println("Music added to player queue:")
for (index, musicPath) in enumerate(musicFiles) {
println("#\(index + 1) - \(musicPath.lastPathComponent)")
}
println("\n===== Type \"start\" to begin.")
}
private func logStart() {
println("\n=====> 🎵 RyCooder has taken the stage...")
}
private func logInputTips() {
println("\nType \"next\" or \"previous\" to change song.")
println("Type index number to play that song.")
}
private func preparePlayerItemsWithPaths(musicPaths: [String]) -> [AVPlayerItem] {
var items: [AVPlayerItem] = []
for (_ , musicPath) in enumerate(musicPaths) {
let itemURL = NSURL.fileURLWithPath(musicPath)
let playerItem = AVPlayerItem(URL: itemURL)
items.append(playerItem)
}
return items
}
private func musicFilesInDirectory(dirPath: String) -> [String] {
var result: [String] = []
if let directoryEnumerator = NSFileManager.defaultManager().enumeratorAtPath(dirPath) {
let isMusicFile: String -> Bool = {
fileName in
let pathExtension = fileName.pathExtension
return pathExtension == "m4a" ||
pathExtension == "mp3"
}
var fileName: String?
let fileEnumeration: Void -> Void = {
fileName = directoryEnumerator.nextObject() as? String
if let validFileName = fileName {
if isMusicFile(validFileName) {
let fullPath = dirPath.stringByAppendingPathComponent(validFileName)
result.append(fullPath)
}
}
}
fileEnumeration()
while fileName != nil {
fileEnumeration()
}
}
return result
}
}
// Script
let path = NSFileManager.defaultManager().currentDirectoryPath
AudioPlayer(contentPath: path).start()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment