Skip to content

Commit f666332

Browse files
committed
[feat]: funcs to methods
1 parent 440a0db commit f666332

File tree

4 files changed

+270
-241
lines changed

4 files changed

+270
-241
lines changed

Sources/ScriptToolkit/FileExtension.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@
77

88
import Foundation
99
import Files
10+
import SwiftShell
1011

1112
public extension File {
13+
14+
////////////////////////////////////////////////////////////////////////////////
15+
// MARK: - Duplication
16+
1217
@discardableResult public func createDuplicate(withName newName: String, keepExtension: Bool = true) throws -> File {
1318
guard let parent = parent else {
1419
throw OperationError.renameFailed(self)
@@ -35,4 +40,63 @@ public extension File {
3540
throw OperationError.renameFailed(self)
3641
}
3742
}
43+
44+
////////////////////////////////////////////////////////////////////////////////
45+
// MARK: - Photo Processing
46+
47+
func incorporateFile(using folderRecords: [(Folder, [Int])]) throws {
48+
print("\(path)")
49+
let numberString = nameExcludingExtension.replacingOccurrences(of: "IMG_", with: "")
50+
var lastMaximum: Int?
51+
if let number = Int(numberString) {
52+
53+
var moved = false
54+
for folderRecord in folderRecords {
55+
if let firstIndex = folderRecord.1.first, let lastIndex = folderRecord.1.last, number >= firstIndex, number <= lastIndex {
56+
try move(to: folderRecord.0)
57+
moved = true
58+
break
59+
}
60+
61+
if let unwrappedLastMaximum = lastMaximum, let firstIndex = folderRecord.1.first, unwrappedLastMaximum <= number, firstIndex >= number {
62+
try move(to: folderRecord.0)
63+
moved = true
64+
break
65+
}
66+
67+
lastMaximum = folderRecord.1.last
68+
}
69+
if !moved { print(" unable to process - no appropriate folder") }
70+
}
71+
else {
72+
print(" unable to process")
73+
}
74+
}
75+
76+
////////////////////////////////////////////////////////////////////////////////
77+
// MARK: - Resize image
78+
79+
public func resizeImage(newName: String, size: CGSize) {
80+
run("/usr/local/bin/convert", path, "-resize", "\(size.width)x\(size.height)",newName)
81+
}
82+
83+
public func resizeAt123x(width: Int, height: Int, outputDir: Folder) throws {
84+
print(name)
85+
86+
let res1name = outputDir.path.appendingPathComponent(path: name)
87+
resizeImage(newName: res1name, size: CGSize(width: width, height: height))
88+
89+
let res2name = outputDir.path.appendingPathComponent(path: nameExcludingExtension + "@2x." + (self.extension ?? ""))
90+
resizeImage(newName: res2name, size: CGSize(width: 2 * width, height: 2 * height))
91+
92+
let res3name = outputDir.path.appendingPathComponent(path: nameExcludingExtension + "@3x." + (self.extension ?? ""))
93+
resizeImage(newName: res3name, size: CGSize(width: 3 * width, height: 3 * height))
94+
}
95+
96+
////////////////////////////////////////////////////////////////////////////////
97+
// MARK: - Audio Processing
98+
99+
public func slowDownAudio(newName: String, size: CGSize) {
100+
run("/usr/local/bin/sox", path, "-resize", "\(size.width)x\(size.height)",newName)
101+
}
38102
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
// FileSystemItemExtension.swift
3+
// ScriptToolkit
4+
//
5+
// Created by Dan on 15.02.2019.
6+
//
7+
8+
import Foundation
9+
import Files
10+
import SwiftShell
11+
12+
public extension FileSystem.Item {
13+
14+
public func modificationDate() throws -> Date {
15+
16+
let fileAttributes = try FileManager.default.attributesOfItem(atPath: path) as [FileAttributeKey: Any]
17+
let modificationDate = fileAttributes[.modificationDate] as! Date
18+
19+
return modificationDate
20+
}
21+
22+
////////////////////////////////////////////////////////////////////////////////
23+
24+
public func tag(copy: Bool) throws {
25+
let date = try modificationDate()
26+
let suffix = ScriptToolkit.dateFormatter.string(from: date)
27+
for letter in "abcdefghijklmnopqrstuvwxyz" {
28+
29+
switch FileManager.default.itemKind(atPath: path)! {
30+
31+
case .file:
32+
let file = try File(path: path)
33+
let newPath = (file.parent?.path ?? "./")
34+
let newName = file.nameExcludingExtension + "(\(suffix + String(letter)))" + "." + (file.extension ?? "")
35+
36+
if !FileManager.default.fileExists(atPath: newPath + newName) {
37+
if copy {
38+
try file.createDuplicate(withName: newName)
39+
}
40+
else {
41+
try file.rename(to: newName)
42+
}
43+
return
44+
}
45+
46+
case .folder:
47+
let folder = try Folder(path: path)
48+
let newPath = (folder.parent?.path ?? "./")
49+
var newName = folder.nameExcludingExtension + "(\(suffix + String(letter)))"
50+
51+
if let ext = folder.extension {
52+
newName += "." + ext
53+
}
54+
55+
if !FileManager.default.fileExists(atPath: newPath + newName) {
56+
if copy {
57+
try folder.createDuplicate(withName: newName)
58+
}
59+
else {
60+
try folder.rename(to: newName)
61+
}
62+
return
63+
}
64+
}
65+
}
66+
}
67+
68+
69+
}

Sources/ScriptToolkit/FolderExtension.swift

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
import Foundation
99
import Files
10+
import SwiftShell
1011

1112
public extension Folder {
13+
1214
@discardableResult public func createDuplicate(withName newName: String, keepExtension: Bool = true) throws -> Folder {
1315
guard let parent = parent else {
1416
throw OperationError.renameFailed(self)
@@ -37,5 +39,140 @@ public extension Folder {
3739
throw OperationError.renameFailed(self)
3840
}
3941
}
42+
43+
////////////////////////////////////////////////////////////////////////////////
44+
// MARK: - Flatten folder structure
45+
46+
public func flattenFolderStructure(inputDir: String, outputDir: String, move: Bool) throws {
47+
let inputFolder = try Folder(path: inputDir)
48+
let outputFolder = try Folder(path: outputDir)
49+
50+
let inputFolderPath = inputFolder.path
51+
let index = inputFolderPath.index(inputFolderPath.startIndex, offsetBy: inputFolderPath.count)
52+
53+
try inputFolder.makeSubfolderSequence(recursive: true).forEach { folder in
54+
print("folder: \(folder), files: \(folder.files)")
55+
let folderPath = folder.path[index...]
56+
let folderPrefix = folderPath.replacingOccurrences(of: "/", with: " ")
57+
58+
for file in folder.files {
59+
if move {
60+
try file.rename(to: folderPrefix + " " + file.name, keepExtension: true)
61+
}
62+
else {
63+
let newFile = try file.copy(to: outputFolder)
64+
try newFile.rename(to: folderPrefix + " " + file.name, keepExtension: true)
65+
}
66+
}
67+
}
68+
}
69+
70+
////////////////////////////////////////////////////////////////////////////////
71+
// MARK: - Sort Photos
72+
73+
public func exifTool(byCameraModel: Bool, processM4V: Bool) throws {
74+
let inputFolder = try Folder(path: path)
75+
76+
print("📷 Processing EXIFtool...")
77+
78+
if byCameraModel {
79+
// Process JPG dirs using exiftool
80+
for dir in inputFolder.subfolders {
81+
run("/usr/local/bin/exiftool","-Directory<Model", "-d", "%Y-%m-%d \(dir.name)", dir.path)
82+
}
83+
84+
// Process JPG using exiftool
85+
for file in inputFolder.files {
86+
run("/usr/local/bin/exiftool" ,"-Directory<Model", "-d", "%Y-%m-%d", file.path)
87+
}
88+
return
89+
}
90+
91+
92+
// Process JPG dirs using exiftool
93+
for dir in inputFolder.subfolders {
94+
run("/usr/local/bin/exiftool","-Directory<DateTimeOriginal", "-d", "%Y-%m-%d \(dir.name)", dir.path)
95+
}
96+
97+
var originalFolders = inputFolder
98+
.subfolders
99+
.filter { matches(for: "^\\d\\d\\d\\d-\\d\\d-\\d\\d.*$", in: $0.name).isEmpty }
100+
101+
originalFolders.append(inputFolder)
102+
103+
// Support of M4V
104+
for dir in originalFolders {
105+
for file in dir.makeFileSequence(recursive: true, includeHidden: true) {
106+
107+
switch file.extension?.lowercased() ?? "" {
108+
case "jpg", "jpeg":
109+
run("/usr/local/bin/exiftool" ,"-Directory<DateTimeOriginal", "-d", "%Y-%m-%d", file.path)
110+
111+
case "png":
112+
run("/usr/local/bin/exiftool" ,"-Directory<DateCreated", "-d", "%Y-%m-%d", file.path)
113+
114+
case "m4v":
115+
if !processM4V {
116+
run("/usr/local/bin/exiftool" ,"-Directory<ContentCreateDate", "-d", "%Y-%m-%d", file.path)
117+
}
118+
119+
case "mp4":
120+
run("/usr/local/bin/exiftool" ,"-Directory<FileAccessDate", "-d", "%Y-%m-%d", file.path)
121+
122+
case "mov":
123+
run("/usr/local/bin/exiftool" ,"-Directory<CreationDate", "-d", "%Y-%m-%d", file.path)
124+
125+
default:
126+
break
127+
}
128+
}
129+
}
130+
}
131+
132+
public func organizePhotos(inputDir: String) throws {
133+
print("📂 Organizing...")
134+
135+
let inputFolder = try Folder(path: inputDir)
136+
var folderRecords = [(Folder, [Int])]()
137+
138+
let sortedSubfolders = inputFolder
139+
.subfolders
140+
.filter { !matches(for: "^\\d\\d\\d\\d-\\d\\d-\\d\\d.*$", in: $0.name).isEmpty }
141+
.sorted { $0.name < $1.name }
142+
143+
for dir in sortedSubfolders {
144+
let indexes = dir.files
145+
.map { $0.nameExcludingExtension.replacingOccurrences(of: "IMG_", with: "") }
146+
.compactMap { Int($0) }
147+
.sorted()
148+
folderRecords.append((dir, indexes))
149+
}
150+
151+
var originalFolders = inputFolder
152+
.subfolders
153+
.filter { matches(for: "^\\d\\d\\d\\d-\\d\\d-\\d\\d.*$", in: $0.name).isEmpty }
154+
155+
originalFolders.append(inputFolder)
156+
157+
for folder in originalFolders {
158+
for file in folder.makeFileSequence(recursive: true, includeHidden: true) {
159+
try file.incorporateFile(using: folderRecords)
160+
}
161+
}
162+
}
163+
164+
////////////////////////////////////////////////////////////////////////////////
165+
// MARK: - Remove Empty Directories
166+
167+
public func removeEmptyDirectories(in folder: Folder) throws {
168+
for subfolder in folder.subfolders {
169+
try removeEmptyDirectories(in: subfolder)
170+
}
171+
172+
if folder.subfolders.count == 0 && folder.files.count == 0 {
173+
print("removed: \(folder.path)")
174+
try folder.delete()
175+
}
176+
}
40177
}
41178

0 commit comments

Comments
 (0)