Skip to content

Instantly share code, notes, and snippets.

@siilver777
Created February 22, 2021 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save siilver777/8c7da92bc0661c3ea0135c753d54b760 to your computer and use it in GitHub Desktop.
Save siilver777/8c7da92bc0661c3ea0135c753d54b760 to your computer and use it in GitHub Desktop.
Command-line Plex folder cleaner, removes SRT files that doesn't match an existing media
//
// main.swift
// PlexCleaner
//
// Created by Jason Pierna on 22/10/2020.
//
import Foundation
struct ValidationError: Error, CustomStringConvertible {
private var message: String
init(_ message: String) {
self.message = message
}
var description: String {
return message
}
}
var path: String!
var dryRun: Bool = false
func validate() throws {
var isDirectory: ObjCBool = false
guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) else {
throw ValidationError("Provided path does not exist.")
}
guard isDirectory.boolValue else {
throw ValidationError("Provided path is not a directory.")
}
}
func run() throws {
getFolders(at: path).forEach(cleanFolder)
}
private func contentOfFolder(at path: String) -> [String] {
let contentsOfFolder = try? FileManager.default.contentsOfDirectory(atPath: path)
.filter { $0 != ".DS_Store" }
.map { "\(path)/\($0)" }
return contentsOfFolder ?? []
}
private func getFolders(at path: String) -> [String] {
let contentsOfFolder = contentOfFolder(at: path)
return contentsOfFolder.filter {
var isDirectory: ObjCBool = false
_ = FileManager.default.fileExists(atPath: $0, isDirectory: &isDirectory)
return isDirectory.boolValue
}
}
private func cleanFolder(folder: String) {
let videoType = "mp4"
let nestedFolders = getFolders(at: folder)
let videoFiles = getFilesOfType(videoType, in: folder)
let srtFiles = getFilesOfType("srt", in: folder)
getUnmatchedSRTFiles(srt: srtFiles, videos: videoFiles, ofType: videoType).forEach(trashFile)
nestedFolders.forEach(cleanFolder)
let contentsOfFolder = contentOfFolder(at: folder)
if contentsOfFolder.count == 0 {
trashFile(filePath: folder)
}
}
private func getFilesOfType(_ type: String, in folder: String) -> [String] {
let contentsOfFolder = contentOfFolder(at: folder)
return contentsOfFolder.filter { $0.hasSuffix(".\(type)") }
}
private func getUnmatchedSRTFiles(srt: [String], videos: [String], ofType type: String) -> [String] {
return srt.filter { srtFile in
return !videos.contains(where: { videoFile in
let videoFilenameWithoutExt = videoFile.replacingOccurrences(of: ".\(type)", with: "")
return srtFile.hasPrefix(videoFilenameWithoutExt)
})
}
}
private func trashFile(filePath: String) {
let url = URL(fileURLWithPath: filePath)
do {
if dryRun {
print("TRASH:", filePath)
} else {
try FileManager.default.trashItem(at: url, resultingItemURL: nil)
}
} catch {
print("Error while trashing \(filePath)", error, error.localizedDescription)
}
}
private func displayUsage() {
}
do {
let args = CommandLine.arguments
guard args.count > 1 else {
throw ValidationError("Missing parameter: <path>")
}
path = args[1]
guard args.count <= 3 else {
throw ValidationError("Too much parameters!")
}
if args.count == 3 {
if args[2] == "--dry-run" {
dryRun = true
} else {
throw ValidationError("Wrong flag. Use --dry-run.")
}
}
try run()
} catch {
print("Error:", error.localizedDescription)
displayUsage()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment