appcask 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.idea/workspace.xml +43 -17
- data/CHANGELOG.md +64 -23
- data/QUICKSTART.md +100 -100
- data/README.md +106 -106
- data/examples/batch_download.rb +15 -13
- data/exe/appcask +2 -1
- data/lib/appcask/local_app.rb +139 -0
- data/lib/appcask/version.rb +1 -1
- data/lib/appcask.rb +240 -229
- data/sig/appcask.rbs +1 -1
- metadata +48 -4
data/README.md
CHANGED
|
@@ -1,159 +1,159 @@
|
|
|
1
|
-
# AppCask - App Store
|
|
1
|
+
# AppCask - App Store Resources Downloader
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
An all-in-one command-line tool for downloading resources of iOS App Store apps
|
|
10
|
+
**Icons • Screenshots • App Description • One-click Packaging**
|
|
11
11
|
|
|
12
|
-
[
|
|
12
|
+
[Features](#-features) • [Installation](#-installation) • [Usage Guide](#-usage-guide) • [Examples](#-usage-examples)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
## ✨
|
|
19
|
-
|
|
20
|
-
### 🎨
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
### 📸
|
|
26
|
-
- iPhone
|
|
27
|
-
- iPad
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
### 📝
|
|
32
|
-
- **TXT
|
|
33
|
-
- **JSON
|
|
34
|
-
- **Markdown
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
### 📦
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
|
|
51
|
-
### 🌍
|
|
52
|
-
- 🇺🇸
|
|
53
|
-
- 🇨🇳
|
|
54
|
-
- 🇯🇵
|
|
55
|
-
- 🇰🇷
|
|
56
|
-
- 🇭🇰
|
|
57
|
-
- 🇹🇼
|
|
58
|
-
- 🇬🇧
|
|
59
|
-
- 🇩🇪
|
|
60
|
-
- 🇫🇷
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
### 🎨 Icon Download
|
|
21
|
+
- Supports four sizes: 60x60, 100x100, 512x512, 1024x1024
|
|
22
|
+
- Auto-detect image formats (PNG/JPG/GIF/WEBP)
|
|
23
|
+
- Intelligent file naming
|
|
24
|
+
|
|
25
|
+
### 📸 Screenshot Download
|
|
26
|
+
- iPhone screenshots
|
|
27
|
+
- iPad screenshots
|
|
28
|
+
- Batch download all screenshots
|
|
29
|
+
- Automatic organization and saving
|
|
30
|
+
|
|
31
|
+
### 📝 Export App Information
|
|
32
|
+
- **TXT format** - Human-readable text
|
|
33
|
+
- **JSON format** - Structured data
|
|
34
|
+
- **Markdown format** - Elegant documentation
|
|
35
|
+
|
|
36
|
+
Included information:
|
|
37
|
+
- Basic information (name, developer, Bundle ID)
|
|
38
|
+
- Version information (current version, file size, system requirements)
|
|
39
|
+
- Rating statistics (average rating, number of ratings)
|
|
40
|
+
- Pricing information
|
|
41
|
+
- App description
|
|
42
|
+
- Version update notes
|
|
43
|
+
- Related links
|
|
44
|
+
|
|
45
|
+
### 📦 One-click Full Package
|
|
46
|
+
Download all resources for the app, including:
|
|
47
|
+
- Icons in all sizes
|
|
48
|
+
- Screenshots for all devices
|
|
49
|
+
- Complete app information (in 3 formats)
|
|
50
|
+
|
|
51
|
+
### 🌍 Multiregion Support
|
|
52
|
+
- 🇺🇸 United States (US)
|
|
53
|
+
- 🇨🇳 China (CN)
|
|
54
|
+
- 🇯🇵 Japan (JP)
|
|
55
|
+
- 🇰🇷 Korea (KR)
|
|
56
|
+
- 🇭🇰 Hong Kong (HK)
|
|
57
|
+
- 🇹🇼 Taiwan (TW)
|
|
58
|
+
- 🇬🇧 United Kingdom (GB)
|
|
59
|
+
- 🇩🇪 Germany (DE)
|
|
60
|
+
- 🇫🇷 France (FR)
|
|
61
61
|
|
|
62
62
|
---
|
|
63
63
|
|
|
64
|
-
## 📦
|
|
64
|
+
## 📦 Installation
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
gem install appcask
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
Or from source:
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
73
|
git clone https://github.com/yourusername/appcask.git
|
|
74
74
|
cd appcask
|
|
75
75
|
bundle install
|
|
76
76
|
rake build
|
|
77
|
-
gem install pkg/appcask-
|
|
77
|
+
gem install pkg/appcask-0.6.0.gem
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
---
|
|
81
81
|
|
|
82
|
-
## 🚀
|
|
82
|
+
## 🚀 Quick Start
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
#
|
|
85
|
+
# Interactive mode
|
|
86
86
|
appcask
|
|
87
87
|
|
|
88
|
-
#
|
|
88
|
+
# Quick search
|
|
89
89
|
appcask "Instagram"
|
|
90
90
|
|
|
91
|
-
#
|
|
92
|
-
appcask "
|
|
91
|
+
# Specify region
|
|
92
|
+
appcask "WeChat" cn
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
###
|
|
95
|
+
### Full Demo
|
|
96
96
|
|
|
97
97
|
```
|
|
98
98
|
$ appcask "Twitter"
|
|
99
99
|
|
|
100
100
|
╔═══════════════════════════════════════════╗
|
|
101
|
-
║ AppCask - App
|
|
102
|
-
║
|
|
101
|
+
║ AppCask - App Resources Downloader ║
|
|
102
|
+
║ v0.6.0 ║
|
|
103
103
|
╚═══════════════════════════════════════════╝
|
|
104
104
|
|
|
105
|
-
🔍
|
|
105
|
+
🔍 Searching for "Twitter"...
|
|
106
106
|
|
|
107
|
-
📋
|
|
107
|
+
📋 Found 3 results:
|
|
108
108
|
|
|
109
109
|
[0] X
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
Developer: X Corp. | Version: 10.31
|
|
111
|
+
Price: Free | Rating: ⭐ 4.2
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
Please choose (0-2, or press q to quit): 0
|
|
114
114
|
|
|
115
|
-
✅
|
|
115
|
+
✅ Selected: X
|
|
116
116
|
|
|
117
|
-
📦
|
|
118
|
-
[1]
|
|
119
|
-
[2]
|
|
120
|
-
[3]
|
|
121
|
-
[4]
|
|
117
|
+
📦 Choose content to download:
|
|
118
|
+
[1] Icon
|
|
119
|
+
[2] Screenshots
|
|
120
|
+
[3] Description
|
|
121
|
+
[4] Full package (Icons + Screenshots + Description)
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
Please choose (1-4): 4
|
|
124
124
|
|
|
125
|
-
✨
|
|
125
|
+
✨ Download complete!
|
|
126
126
|
📁 ~/Desktop/AppCask Downloads/X
|
|
127
|
-
📊
|
|
127
|
+
📊 Stats: 15 files, total size 8.45 MB
|
|
128
128
|
```
|
|
129
129
|
|
|
130
130
|
---
|
|
131
131
|
|
|
132
|
-
## 📖
|
|
132
|
+
## 📖 Usage Examples
|
|
133
133
|
|
|
134
|
-
###
|
|
134
|
+
### Download Only Icons
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
137
|
appcask "Instagram"
|
|
138
|
-
#
|
|
138
|
+
# Choose: [1] Icons → [3] 1024x1024
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
###
|
|
141
|
+
### Download All Screenshots
|
|
142
142
|
|
|
143
143
|
```bash
|
|
144
|
-
appcask "
|
|
145
|
-
#
|
|
144
|
+
appcask "Honor of Kings" cn
|
|
145
|
+
# Choose: [2] Screenshots → all
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
###
|
|
148
|
+
### Export App Information
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
151
|
appcask "Notion"
|
|
152
|
-
#
|
|
153
|
-
#
|
|
152
|
+
# Choose: [3] Description
|
|
153
|
+
# Output: TXT + JSON + Markdown
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
-
###
|
|
156
|
+
### Batch Download
|
|
157
157
|
|
|
158
158
|
```ruby
|
|
159
159
|
#!/usr/bin/env ruby
|
|
@@ -168,7 +168,7 @@ end
|
|
|
168
168
|
|
|
169
169
|
---
|
|
170
170
|
|
|
171
|
-
## 📂
|
|
171
|
+
## 📂 File Structure
|
|
172
172
|
|
|
173
173
|
```
|
|
174
174
|
AppCask Downloads/
|
|
@@ -188,57 +188,57 @@ AppCask Downloads/
|
|
|
188
188
|
|
|
189
189
|
---
|
|
190
190
|
|
|
191
|
-
## 🔧
|
|
191
|
+
## 🔧 Advanced Features
|
|
192
192
|
|
|
193
|
-
### Debug
|
|
193
|
+
### Debug Mode
|
|
194
194
|
|
|
195
195
|
```bash
|
|
196
196
|
DEBUG=1 appcask "AppName"
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
###
|
|
199
|
+
### Quick Operations
|
|
200
200
|
|
|
201
|
-
-
|
|
202
|
-
- **q** -
|
|
203
|
-
- **Ctrl+C** -
|
|
201
|
+
- **Enter** - Default option
|
|
202
|
+
- **q** - Quit
|
|
203
|
+
- **Ctrl+C** - Interrupt
|
|
204
204
|
|
|
205
205
|
---
|
|
206
206
|
|
|
207
|
-
## 🐛
|
|
207
|
+
## 🐛 Troubleshooting
|
|
208
208
|
|
|
209
|
-
**Q:
|
|
210
|
-
A:
|
|
209
|
+
**Q: Can't find the app?**
|
|
210
|
+
A: Check spelling and try switching regions
|
|
211
211
|
|
|
212
|
-
**Q:
|
|
213
|
-
A:
|
|
212
|
+
**Q: Icon size is wrong?**
|
|
213
|
+
A: Some apps do not support 1024x1024
|
|
214
214
|
|
|
215
|
-
**Q:
|
|
216
|
-
A:
|
|
215
|
+
**Q: Network timeout?**
|
|
216
|
+
A: Check network, use VPN
|
|
217
217
|
|
|
218
|
-
**Q:
|
|
218
|
+
**Q: Where are files saved?**
|
|
219
219
|
A: `~/Desktop/AppCask Downloads/`
|
|
220
220
|
|
|
221
221
|
---
|
|
222
222
|
|
|
223
|
-
## 🎯
|
|
223
|
+
## 🎯 Roadmap
|
|
224
224
|
|
|
225
|
-
- [ ] macOS App Store
|
|
226
|
-
- [ ]
|
|
227
|
-
- [ ]
|
|
228
|
-
- [ ] Web
|
|
225
|
+
- [ ] macOS App Store support
|
|
226
|
+
- [ ] App reviews download
|
|
227
|
+
- [ ] Batch download mode
|
|
228
|
+
- [ ] Web interface
|
|
229
229
|
|
|
230
230
|
---
|
|
231
231
|
|
|
232
|
-
## 📄
|
|
232
|
+
## 📄 License
|
|
233
233
|
|
|
234
234
|
MIT License
|
|
235
235
|
|
|
236
236
|
---
|
|
237
237
|
|
|
238
|
-
## 📮
|
|
238
|
+
## 📮 Contact
|
|
239
239
|
|
|
240
240
|
- GitHub: [@yourusername](https://github.com/yourusername)
|
|
241
|
-
- Issues: [
|
|
241
|
+
- Issues: [Feedback issues](https://github.com/yourusername/appcask/issues)
|
|
242
242
|
|
|
243
243
|
---
|
|
244
244
|
|
data/examples/batch_download.rb
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
# AppCask batch download example
|
|
3
5
|
# Usage: ruby examples/batch_download.rb
|
|
4
6
|
|
|
5
|
-
require
|
|
7
|
+
require "appcask"
|
|
6
8
|
|
|
7
9
|
# Define the list of apps to download
|
|
8
10
|
apps = [
|
|
9
|
-
{ name:
|
|
10
|
-
{ name:
|
|
11
|
-
{ name:
|
|
12
|
-
{ name:
|
|
13
|
-
{ name:
|
|
11
|
+
{ name: "Instagram", region: "us", description: "Social media app" },
|
|
12
|
+
{ name: "Twitter", region: "us", description: "Microblogging platform" },
|
|
13
|
+
{ name: "WeChat", region: "cn", description: "Instant messaging app" },
|
|
14
|
+
{ name: "LINE", region: "jp", description: "Messaging app popular in Japan" },
|
|
15
|
+
{ name: "KakaoTalk", region: "kr", description: "Messaging app popular in Korea" }
|
|
14
16
|
]
|
|
15
17
|
|
|
16
|
-
puts
|
|
17
|
-
puts
|
|
18
|
-
puts
|
|
18
|
+
puts "=" * 50
|
|
19
|
+
puts "AppCask Batch Download Script"
|
|
20
|
+
puts "=" * 50
|
|
19
21
|
puts "\nPreparing to download resources for #{apps.size} apps...\n\n"
|
|
20
22
|
|
|
21
23
|
apps.each_with_index do |app, index|
|
|
22
24
|
puts "\n[#{index + 1}/#{apps.size}] Processing: #{app[:name]} (#{app[:description]})"
|
|
23
|
-
puts
|
|
25
|
+
puts "-" * 50
|
|
24
26
|
|
|
25
27
|
# Set command-line arguments
|
|
26
28
|
ARGV.clear
|
|
@@ -38,6 +40,6 @@ apps.each_with_index do |app, index|
|
|
|
38
40
|
sleep 2 unless index == apps.size - 1
|
|
39
41
|
end
|
|
40
42
|
|
|
41
|
-
puts "\n"
|
|
42
|
-
puts
|
|
43
|
-
puts
|
|
43
|
+
puts "\n#{"=" * 50}"
|
|
44
|
+
puts "Batch download completed!"
|
|
45
|
+
puts "=" * 50
|
data/exe/appcask
CHANGED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cfpropertylist"
|
|
4
|
+
require "icns"
|
|
5
|
+
require "mini_magick"
|
|
6
|
+
require "fileutils"
|
|
7
|
+
|
|
8
|
+
module AppCask
|
|
9
|
+
class LocalApp
|
|
10
|
+
APPS_DIR = "/Applications"
|
|
11
|
+
OUTPUT_BASE = File.join(Dir.home, "Desktop", "AppCask Downloads", "Local Apps")
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def fetch_all
|
|
15
|
+
puts "\nFetching local app icons..."
|
|
16
|
+
puts "Source: #{APPS_DIR}"
|
|
17
|
+
puts "Output: #{OUTPUT_BASE}\n\n"
|
|
18
|
+
|
|
19
|
+
FileUtils.mkdir_p(OUTPUT_BASE)
|
|
20
|
+
|
|
21
|
+
apps = Dir.glob(File.join(APPS_DIR, "*.app"))
|
|
22
|
+
total = apps.count
|
|
23
|
+
success = 0
|
|
24
|
+
skipped = 0
|
|
25
|
+
|
|
26
|
+
apps.each_with_index do |app_path, index|
|
|
27
|
+
app_name = File.basename(app_path, ".app")
|
|
28
|
+
print "\r[#{index + 1}/#{total}] Processing: #{app_name.ljust(30)}"
|
|
29
|
+
|
|
30
|
+
result = extract_icon(app_path)
|
|
31
|
+
if result
|
|
32
|
+
success += 1
|
|
33
|
+
else
|
|
34
|
+
skipped += 1
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
puts "\n\n#{"=" * 50}"
|
|
39
|
+
puts "Done!"
|
|
40
|
+
puts " Total apps: #{total}"
|
|
41
|
+
puts " Extracted: #{success}"
|
|
42
|
+
puts " Skipped: #{skipped}"
|
|
43
|
+
puts " Output: #{OUTPUT_BASE}"
|
|
44
|
+
puts "=" * 50
|
|
45
|
+
|
|
46
|
+
# Open folder on macOS
|
|
47
|
+
return unless RUBY_PLATFORM.include?("darwin")
|
|
48
|
+
|
|
49
|
+
print "\nOpen the folder? (y/n): "
|
|
50
|
+
response = $stdin.gets
|
|
51
|
+
system("open '#{OUTPUT_BASE}'") if response&.strip&.downcase == "y"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def extract_icon(app_path)
|
|
57
|
+
# 1. Read Info.plist
|
|
58
|
+
plist_path = File.join(app_path, "Contents", "Info.plist")
|
|
59
|
+
return nil unless File.exist?(plist_path)
|
|
60
|
+
|
|
61
|
+
plist = CFPropertyList::List.new(file: plist_path)
|
|
62
|
+
info = CFPropertyList.native_types(plist.value)
|
|
63
|
+
|
|
64
|
+
# 2. Get icon file name
|
|
65
|
+
icon_name = info["CFBundleTypeIconFile"] || info["CFBundleIconFile"] || info["CFBundleIconName"]
|
|
66
|
+
return nil unless icon_name
|
|
67
|
+
|
|
68
|
+
icon_name += ".icns" unless icon_name.end_with?(".icns")
|
|
69
|
+
|
|
70
|
+
# 3. Find .icns file
|
|
71
|
+
icns_path = File.join(app_path, "Contents", "Resources", icon_name)
|
|
72
|
+
return nil unless File.exist?(icns_path)
|
|
73
|
+
|
|
74
|
+
# 4. Parse .icns and extract largest PNG
|
|
75
|
+
reader = Icns::Reader.new(icns_path)
|
|
76
|
+
|
|
77
|
+
# Priority: 1024 → 512 → 256 → 128
|
|
78
|
+
[1024, 512, 256, 128].each do |size|
|
|
79
|
+
data = reader.image(size: size)
|
|
80
|
+
next unless data
|
|
81
|
+
next unless png?(data)
|
|
82
|
+
|
|
83
|
+
# 5. Save as 512x512 PNG
|
|
84
|
+
output_path = output_path(app_path)
|
|
85
|
+
save_as_512(data, size, output_path)
|
|
86
|
+
return output_path
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
nil
|
|
90
|
+
rescue StandardError
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def png?(data)
|
|
95
|
+
png_magic = [0x89, 0x50, 0x4e, 0x47].pack("C4")
|
|
96
|
+
data[0..3] == png_magic
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def save_as_512(data, original_size, output_path)
|
|
100
|
+
if original_size == 512
|
|
101
|
+
# Direct write
|
|
102
|
+
File.write(output_path, data)
|
|
103
|
+
else
|
|
104
|
+
# Need to resize - use mini_magick
|
|
105
|
+
resize_with_mini_magick(data, output_path)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def resize_with_mini_magick(data, output_path)
|
|
110
|
+
# Write to temp file, resize, then save
|
|
111
|
+
tmp_input = File.join(Dir.tmpdir, "appcask_input_#{Process.pid}.png")
|
|
112
|
+
tmp_output = File.join(Dir.tmpdir, "appcask_output_#{Process.pid}.png")
|
|
113
|
+
|
|
114
|
+
File.write(tmp_input, data)
|
|
115
|
+
|
|
116
|
+
image = MiniMagick::Image.new(tmp_input)
|
|
117
|
+
image.resize "512x512"
|
|
118
|
+
image.format "png"
|
|
119
|
+
image.write(tmp_output)
|
|
120
|
+
|
|
121
|
+
FileUtils.cp(tmp_output, output_path)
|
|
122
|
+
ensure
|
|
123
|
+
FileUtils.rm_f(tmp_input) if tmp_input
|
|
124
|
+
FileUtils.rm_f(tmp_output) if tmp_output
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def output_path(app_path)
|
|
128
|
+
app_name = File.basename(app_path, ".app")
|
|
129
|
+
filename = "#{sanitize_app_name(app_name)}.png"
|
|
130
|
+
File.join(OUTPUT_BASE, filename)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def sanitize_app_name(name)
|
|
134
|
+
# Keep Chinese characters, convert to lowercase, replace spaces with hyphens
|
|
135
|
+
name.downcase.gsub(/\s+/, "-").gsub(/[^a-z0-9\u4e00-\u9fff-]/, "").gsub(/^-|-$/, "")
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
data/lib/appcask/version.rb
CHANGED