micompress 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5bb22286474daaaaa3282785ff4887fa39cc071050e9a90a29834cc2c2d06eb2
4
+ data.tar.gz: 22e556306149811d188f7e278100898e33400957a2fc6de1c29c6cdb5f567286
5
+ SHA512:
6
+ metadata.gz: f8814ecd628c6ebe11627886effaf7d0b18b3a082787b6b7ed5efd8900b43ed1e3211d4f12ff33b4e8055743a563cfdd8a2726d126e68d46de285563a76c34d0
7
+ data.tar.gz: c4af22c5e3561ab916574050bb7aa4d7fd32ef8b3b6667291baabc0c1e3127bd47c2fc3ebab99f03d0c4890d09311855153eccfffcae310f24f4327635922638
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MiCompress (https://micompress.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # micompress (Ruby)
2
+
3
+ > Compress **images, PDFs, video, audio, documents & archives** from the command line or Ruby.
4
+ > Powered by [**micompress.com**](https://micompress.com/?utm_source=rubygems&utm_medium=cli&utm_campaign=extend).
5
+
6
+ Zero runtime dependencies (standard library only).
7
+
8
+ ```bash
9
+ gem install micompress
10
+ ```
11
+
12
+ ## CLI
13
+
14
+ ```bash
15
+ micompress photo.jpg
16
+ micompress *.png --preset extreme
17
+ micompress report.pdf -o out/
18
+ ```
19
+
20
+ ## Library
21
+
22
+ ```ruby
23
+ require "micompress"
24
+
25
+ bytes, meta = Micompress.compress_file("photo.jpg", preset: "extreme")
26
+ File.binwrite("photo-min.jpg", bytes)
27
+ puts "#{meta['savedPercent']}% smaller"
28
+ ```
29
+
30
+ ## Build & publish
31
+
32
+ Requires Ruby (not available on this build server):
33
+
34
+ ```bash
35
+ gem build micompress.gemspec
36
+ gem push micompress-0.1.0.gem
37
+ ```
38
+
39
+ ## License
40
+
41
+ MIT © [MiCompress](https://micompress.com)
data/exe/micompress ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+ require "micompress"
6
+
7
+ options = { preset: "recommended", base_url: Micompress::DEFAULT_BASE, suffix: "-min" }
8
+ parser = OptionParser.new do |o|
9
+ o.banner = "Usage: micompress <file...> [options]"
10
+ o.on("-o", "--output DIR", "Output directory") { |v| options[:output] = v }
11
+ o.on("-p", "--preset NAME", %w[lossless recommended extreme], "lossless|recommended|extreme") { |v| options[:preset] = v }
12
+ o.on("--max-width PX", Integer, "Resize images to max width") { |v| options[:max_width] = v }
13
+ o.on("--token JWT", "Auth token for higher limits") { |v| options[:token] = v }
14
+ o.on("--api URL", "API base URL") { |v| options[:base_url] = v }
15
+ o.on("--suffix STR", "Default output suffix (default -min)") { |v| options[:suffix] = v }
16
+ o.on("-q", "--quiet", "Only print errors") { options[:quiet] = true }
17
+ o.on("-v", "--version", "Show version") { puts Micompress::VERSION; exit }
18
+ o.on("-h", "--help", "Show help") { puts o; exit }
19
+ end
20
+ files = parser.parse(ARGV)
21
+
22
+ if files.empty?
23
+ puts parser
24
+ exit 1
25
+ end
26
+
27
+ def human(n)
28
+ return "?" if n.nil?
29
+ units = %w[B KB MB GB]
30
+ v = n.to_f
31
+ i = 0
32
+ while v >= 1024 && i < units.length - 1
33
+ v /= 1024
34
+ i += 1
35
+ end
36
+ i.zero? ? "#{v.round}#{units[i]}" : format("%.1f%s", v, units[i])
37
+ end
38
+
39
+ def out_path(input, options)
40
+ base = File.basename(input)
41
+ if options[:output]
42
+ require "fileutils"
43
+ FileUtils.mkdir_p(options[:output])
44
+ return File.join(options[:output], base)
45
+ end
46
+ ext = File.extname(base)
47
+ stem = base[0...(base.length - ext.length)]
48
+ File.join(File.dirname(input), "#{stem}#{options[:suffix]}#{ext}")
49
+ end
50
+
51
+ failed = 0
52
+ files.each do |file|
53
+ begin
54
+ bytes, meta = Micompress.compress_file(
55
+ file, base_url: options[:base_url], preset: options[:preset],
56
+ max_width: options[:max_width], token: options[:token]
57
+ )
58
+ dest = out_path(file, options)
59
+ File.binwrite(dest, bytes)
60
+ unless options[:quiet]
61
+ puts "✔ #{file} → #{dest} #{human(meta['inputSize'])} → #{human(meta['outputSize'])} (-#{meta['savedPercent']}%)"
62
+ end
63
+ rescue Micompress::Error, SystemCallError => e
64
+ failed += 1
65
+ warn "✗ #{file}: #{e.message}"
66
+ end
67
+ end
68
+
69
+ exit(failed.zero? ? 0 : 1)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micompress
4
+ # File extension -> MiCompress category. Mirrors services/formatDetector.js.
5
+ EXT_CATEGORY = {
6
+ "jpg" => "image", "jpeg" => "image", "png" => "image", "webp" => "image", "gif" => "image",
7
+ "bmp" => "image", "tiff" => "image", "tif" => "image", "avif" => "image", "svg" => "image",
8
+ "heic" => "image", "heif" => "image", "ico" => "image", "psd" => "image", "dng" => "image",
9
+ "raw" => "image", "cr2" => "image", "arw" => "image", "nef" => "image",
10
+ "mp4" => "video", "mov" => "video", "avi" => "video", "mkv" => "video", "webm" => "video",
11
+ "mpeg" => "video", "mpg" => "video", "m4v" => "video", "wmv" => "video", "flv" => "video", "3gp" => "video",
12
+ "mp3" => "audio", "wav" => "audio", "flac" => "audio", "ogg" => "audio", "m4a" => "audio",
13
+ "aac" => "audio", "wma" => "audio", "opus" => "audio",
14
+ "pdf" => "pdf",
15
+ "csv" => "document", "json" => "document", "xml" => "document", "md" => "document",
16
+ "odt" => "document", "ods" => "document", "docx" => "document", "doc" => "document",
17
+ "ppt" => "document", "pptx" => "document", "xls" => "document", "xlsx" => "document",
18
+ "epub" => "document", "html" => "document", "htm" => "document", "hwp" => "document",
19
+ "txt" => "document", "rtf" => "document",
20
+ "zip" => "archive", "7z" => "archive", "tar" => "archive", "gz" => "archive", "tgz" => "archive", "rar" => "archive"
21
+ }.freeze
22
+
23
+ CATEGORY_PATH = {
24
+ "image" => "image-compressors", "video" => "video-compressors", "audio" => "audio-compressors",
25
+ "document" => "document-compressors", "pdf" => "pdf-compressors", "archive" => "archive-compressors"
26
+ }.freeze
27
+
28
+ module_function
29
+
30
+ def ext_of(filename)
31
+ ext = File.extname(filename.to_s).sub(".", "").downcase
32
+ ext.empty? ? nil : ext
33
+ end
34
+
35
+ def category_for(filename)
36
+ EXT_CATEGORY[ext_of(filename)]
37
+ end
38
+
39
+ def endpoint_path(category)
40
+ CATEGORY_PATH[category]
41
+ end
42
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
6
+ require_relative "categories"
7
+
8
+ module Micompress
9
+ DEFAULT_BASE = "https://micompress.com"
10
+ UTM = "utm_source=rubygems&utm_medium=cli&utm_campaign=extend"
11
+ USER_AGENT = "micompress-ruby/#{VERSION} (+https://micompress.com)"
12
+
13
+ class Error < StandardError; end
14
+
15
+ # Compress a single file. Returns [bytes, meta_hash].
16
+ def self.compress_file(path, base_url: DEFAULT_BASE, preset: "recommended", max_width: nil, token: nil)
17
+ base = base_url.chomp("/")
18
+ filename = File.basename(path)
19
+ category = category_for(filename)
20
+ raise Error, "Unsupported file type \".#{ext_of(filename) || '?'}\". See #{base}/all-tools" unless category
21
+ raise Error, "File not found: #{path}" unless File.file?(path)
22
+
23
+ uri = URI("#{base}/#{endpoint_path(category)}/compress")
24
+ req = Net::HTTP::Post.new(uri)
25
+ req["Accept"] = "application/json"
26
+ req["User-Agent"] = USER_AGENT
27
+ req["Authorization"] = "Bearer #{token}" if token
28
+
29
+ form = [["file", File.open(path, "rb"), { filename: filename, content_type: "application/octet-stream" }],
30
+ ["preset", preset]]
31
+ form << ["maxWidth", max_width.to_s] if max_width
32
+ req.set_form(form, "multipart/form-data")
33
+
34
+ json = request_json(uri, req)
35
+ unless json["success"] && json["downloadUrl"]
36
+ raise Error, (json["error"] || "Compression failed")
37
+ end
38
+
39
+ bytes = download("#{base}#{json['downloadUrl']}", token)
40
+ [bytes, json]
41
+ end
42
+
43
+ def self.request_json(uri, req)
44
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
45
+ http.read_timeout = 120
46
+ http.request(req)
47
+ end
48
+ JSON.parse(res.body)
49
+ rescue JSON::ParserError
50
+ raise Error, "Unexpected response (HTTP #{res&.code})"
51
+ end
52
+
53
+ def self.download(url, token)
54
+ uri = URI(url)
55
+ req = Net::HTTP::Get.new(uri)
56
+ req["User-Agent"] = USER_AGENT
57
+ req["Authorization"] = "Bearer #{token}" if token
58
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
59
+ http.read_timeout = 120
60
+ http.request(req)
61
+ end
62
+ raise Error, "Download failed (HTTP #{res.code})" unless res.is_a?(Net::HTTPSuccess)
63
+ res.body
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micompress
4
+ VERSION = "0.1.0"
5
+ end
data/lib/micompress.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "micompress/version"
4
+ require_relative "micompress/categories"
5
+ require_relative "micompress/client"
6
+
7
+ module Micompress
8
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: micompress
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - MiCompress
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-07-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A tiny command-line client and library for the micompress.com compression
14
+ API.
15
+ email:
16
+ executables:
17
+ - micompress
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - LICENSE
22
+ - README.md
23
+ - exe/micompress
24
+ - lib/micompress.rb
25
+ - lib/micompress/categories.rb
26
+ - lib/micompress/client.rb
27
+ - lib/micompress/version.rb
28
+ homepage: https://micompress.com
29
+ licenses:
30
+ - MIT
31
+ metadata:
32
+ homepage_uri: https://micompress.com
33
+ source_code_uri: https://github.com/micompress/micompress-ruby
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '2.7'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 3.3.15
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: Compress images, PDFs, video, audio, documents & archives via micompress.com
53
+ test_files: []