convert2ascii 0.1.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37a56a08059d638a7c1e406248484e8fb3ca0fd1d494c8676ac5cc1119e15eeb
4
- data.tar.gz: 3cbf2abb2d13a8a1d1091128852584d35d073ac8b1d7a056dc856916d913582d
3
+ metadata.gz: aa45c05c40f8dbd46f9bae8e89b7e83123f38ac7ae413051db32e5a0e8562d08
4
+ data.tar.gz: f0c0fca60c7dd724b66e226975784fe0d834683533ad734dc349ab4d8b288925
5
5
  SHA512:
6
- metadata.gz: e1dcecc188abc4c1a5916199f91fba45fa1ee70d6fe25427a27b502e5c41fa57fe3c3dedf4cd0a7f3402ac36f132e44f87874ada185d0dc374d78eb29fd9e389
7
- data.tar.gz: aedcc98597e9ef05af5c26435384cb72741b2e97cca92df0e1e2b44e9314f0f2a465f5a070503db29a5bc16bb0cc97933ab4ab7a9b9d21f44179f9df923bc6f9
6
+ metadata.gz: 906c707016273c0988157544eddef58f562fe02b767a626a4124f3dd0e389b91521bfc2685c426cdac716f1a581c8990da0d11e502c73b7595a19c9aefa12074
7
+ data.tar.gz: 3ef8a737f983d57966516fb980836f6e4da64dd45c6dc0dafa15ad05951e36d89d8cdd83b7cf49a4a2c8507270e53500091dd0a304af3742625985a3f3f8b6b6
data/README.md CHANGED
@@ -1,63 +1,101 @@
1
- # Convert2ASCII
1
+ # Convert2Ascii
2
2
 
3
3
  Convert Image/Video to ASCII art.
4
4
 
5
5
 
6
+ ## Intro
7
+
8
+ convert2ascii provides two executable commands:
9
+
10
+ * image2ascii: transform picture to ascii art and display in terminal.
11
+ * video2ascii: transform video to ascii art, you can save or play it in terminal.
12
+
13
+ It also provides classes as a gem:
14
+
15
+ * Convert2Ascii::Image2Ascii
16
+ * Convert2Ascii::Video2Ascii
17
+
18
+ you can use it in your code and make your own ascii art !
19
+
20
+
6
21
  ## Test pass
7
22
 
8
23
  * MacOS 15.2 ✅
9
24
  * Ubuntu 24.04 ✅
10
25
  * Windows 11 ❌
11
-
26
+ * Docker ✅
12
27
 
13
28
  ## Example
14
29
 
30
+ * Black Myth: Wukong
31
+
15
32
  ![example](./example/wukong.jpg)
16
33
 
17
- ![neo](./example/neo.gif)
34
+ * The Matrix: Neo
18
35
 
19
- ![example2](./example/doulai.jpg)
36
+ ![neo](./example/neo.gif)
20
37
 
21
38
  ## Prerequisites
22
39
 
23
- * Ruby3+
40
+ * Ruby 3+
24
41
  * ImageMagick ([Download here](https://imagemagick.org/script/download.php))
25
42
  * ffmpeg ([Download here](https://www.ffmpeg.org/))
26
43
 
44
+ # How to use
45
+
46
+ ## Try in Docker
47
+
48
+ `$ docker run -it -v $(pwd):/app mark24code/convert2ascii bash -c "cd /app && exec bash"`
27
49
 
28
- ## Executable command
50
+ > `$(pwd)` can be changed to your local path. Here, use your working path.
51
+
52
+ ```bash
53
+ # image
54
+ image2ascii -i </path/to/image>
55
+
56
+ # video
57
+ video2ascii -i </path/to/video.mp4>
58
+ ```
59
+
60
+
61
+ ## Install
62
+
63
+ `$ gem install convert2ascii`
64
+
65
+
66
+ ## Executable commands
29
67
 
30
68
  ### image2ascii
31
69
 
32
- Make image to ascii art in your terminal.
70
+ Convert an image to ascii art.
33
71
 
34
72
  ```bash
35
73
  image2ascii -h
36
74
  Usage: image2ascii [options]
37
- --version verison
75
+ --version version
38
76
  -i, --image=URI image uri (required)
39
77
  -w, --width=WIDTH image width (integer)
40
- -s, --style=STYLE ascii style: ['color'| 'text']
41
- -b, --block ascii color style use BLOCK or not [ true | false ]
78
+ -s, --style=STYLE ascii style: 'color'/'text'
79
+ -b, --block ascii color style use BLOCK or not true/false
42
80
  ```
43
81
 
44
82
  ### video2ascii
45
83
 
46
- Make image to ascii art in your terminal.
84
+ Convert a video to ascii art.
47
85
 
48
86
  ```bash
49
87
  Usage: video2ascii [options]
50
88
 
51
- * default will generate and play without save.
52
- * -p will just play ascii frames dir, and ignore -i, -o others options. --loop will play loop
89
+ * By default, it will generate and play without saving.
90
+ * The -p option will just play the ascii frames within the directory, and ignore -i, -o other options. --loop will play loop
53
91
  * -i,-o will just generate and output frames and ignore others options
54
- --version verison
92
+ --version version
55
93
  -i, --input=URI video uri (required)
56
94
  -w, --width=WIDTH video width (integer)
57
95
  -s, --style=STYLE ascii style: ['color'| 'text']
58
96
  -b, --block ascii color style use BLOCK or not [ true | false ]
59
- -o, --ouput=OUTPUT save ascii frame to output dirname
60
- -p, --play_dir=PLAY_DIRNAME input ascii frames dirname to play
97
+ -o, --ouput=OUTPUT save ascii frames to the output directory
98
+ -p, --play_dir=PLAY_DIRNAME input the ascii frames directory to play
61
99
  --loop
62
100
  ```
63
101
 
@@ -108,6 +146,7 @@ ascii.generate.play
108
146
 
109
147
  ```
110
148
 
149
+
111
150
  ## Inspired by
112
151
 
113
152
  * [michaelkofron/image2ascii](https://github.com/michaelkofron/image2ascii)
data/Rakefile CHANGED
@@ -14,6 +14,19 @@ task :build_rdoc do
14
14
  system("rdoc build")
15
15
  end
16
16
 
17
+ desc "Build Docker image"
18
+ task :build_docker do
19
+ system("docker build . -t mark24code/convert2ascii:latest")
20
+ end
21
+
22
+ desc "Push Docker image"
23
+ task :push_docker do
24
+ system("docker push mark24code/convert2ascii:latest")
25
+ end
17
26
 
27
+ desc "Run in docker"
28
+ task :run_in_docker do
29
+ system("docker run -it -v $(pwd):/app mark24code/convert2ascii bash -c \"cd /app && exec bash\"")
30
+ end
18
31
 
19
32
  task default: %i[]
data/exe/image2ascii CHANGED
@@ -7,7 +7,7 @@ options = {}
7
7
  OptionParser.new do |parser|
8
8
  parser.banner = "Usage: image2ascii [options]"
9
9
 
10
- parser.on("--version", "verison") do |v|
10
+ parser.on("--version", "version") do |v|
11
11
  puts "convert2ascii/image2ascii: v#{::Convert2Ascii::VERSION}"
12
12
  puts "author: Mark24"
13
13
  puts "mail: mark.zhangyoung@gmail.com"
data/exe/video2ascii CHANGED
@@ -45,7 +45,7 @@ Usage: video2ascii [options]
45
45
  * -p will just play ascii frames dir, and ignore -i, -o others options. --loop will play loop
46
46
  * -i,-o will just generate and output frames and ignore others options
47
47
  DOC
48
- parser.on("--version", "verison") do |v|
48
+ parser.on("--version", "version") do |v|
49
49
  puts "convert2ascii/video2ascii: v#{::Convert2Ascii::VERSION}"
50
50
  puts "author: Mark24"
51
51
  puts "mail: mark.zhangyoung@gmail.com"
@@ -82,11 +82,11 @@ DOC
82
82
  options[:color_block] = color_block || false
83
83
  end
84
84
 
85
- parser.on("-o", "--ouput=OUTPUT", "save ascii frame to output dirname") do |output|
85
+ parser.on("-o", "--ouput=OUTPUT", "save ascii frames to the output directory") do |output|
86
86
  options[:output] = output
87
87
  end
88
88
 
89
- parser.on("-p", "--play_dir=PLAY_DIRNAME", "input ascii frames dirname to play") do |play_dir|
89
+ parser.on("-p", "--play_dir=PLAY_DIRNAME", "input the ascii frames directory to play") do |play_dir|
90
90
  options[:play_dir] = play_dir
91
91
  end
92
92
 
@@ -1,36 +1,34 @@
1
- require "async"
2
- require "async/barrier"
3
- require "async/semaphore"
1
+ require "parallel"
4
2
  require "etc"
3
+ require "rainbow"
5
4
 
6
5
  module Convert2Ascii
7
6
  class MultiTasker
8
7
  def initialize(proc_tasks)
9
8
  @proc_tasks = proc_tasks
10
9
  @count = set_threads_count
10
+ @finished = []
11
+ @time_start = nil
11
12
  end
12
13
 
13
14
  def set_threads_count
14
15
  cpu_threads = (Etc.nprocessors || 1)
15
- cpu_threads = cpu_threads > 4 ? cpu_threads - 1 : cpu_threads
16
+ cpu_threads = cpu_threads > 4 ? cpu_threads - 2 : 1
16
17
  cpu_threads
17
18
  end
18
19
 
19
- def run
20
- barrier = Async::Barrier.new
21
-
22
- Sync do
23
- # Only 10 tasks are created at a time:
24
- semaphore = Async::Semaphore.new(@count, parent: barrier)
20
+ def progress(index)
21
+ @finished << index
22
+ print(Rainbow("\rprocessing... #{sprintf("%.2f", (1.0 * @finished.length / @proc_tasks.length) * 100)} % (time: #{sprintf("%.2f", Time.now - @time_start)} s)").green)
23
+ end
25
24
 
26
- @proc_tasks.map do |proc_task|
27
- semaphore.async do
28
- proc_task.call
29
- end
30
- end.map(&:wait)
31
- ensure
32
- barrier.stop
25
+ def run
26
+ @time_start = Time.now
27
+ results = Parallel.map(@proc_tasks, in_processes: @count, finish: ->(item, index, result) { progress(index) }, finish_in_order: true) do |task|
28
+ task && task.call
33
29
  end
30
+
31
+ results
34
32
  end
35
33
  end
36
34
  end
@@ -2,12 +2,15 @@ require "rainbow"
2
2
  require_relative "./terminal"
3
3
 
4
4
  module Convert2Ascii
5
- class TerminalPlayer
5
+ class TerminalPlayerError < StandardError
6
+ end
6
7
 
8
+ class TerminalPlayer
7
9
  SAFE_SLOW_DELTA = 0.9 # seconds
8
10
  SAFE_FAST_DELTA = 0.2 # seconds
9
11
 
10
12
  attr_accessor :play_loop, :step_duration, :debug
13
+
11
14
  def initialize(**args)
12
15
  @debug = false
13
16
  @audio = args[:audio]
@@ -20,6 +23,13 @@ module Convert2Ascii
20
23
  @backspace_adjust = "\033[A" * (@frames.length + 1)
21
24
 
22
25
  regist_hook
26
+ check_params
27
+ end
28
+
29
+ def check_params
30
+ if @frames.length <= 0
31
+ raise TerminalPlayerError, "\n[Error] frame's length must be >= 0 "
32
+ end
23
33
  end
24
34
 
25
35
  def play
@@ -76,7 +86,7 @@ module Convert2Ascii
76
86
  end
77
87
 
78
88
  def debug_log(var_name)
79
- puts Rainbow('-- debug ----').yellow
89
+ puts Rainbow("-- debug ----").yellow
80
90
  puts "class:"
81
91
  p var_name.class
82
92
  puts "value:"
@@ -84,7 +94,6 @@ module Convert2Ascii
84
94
  end
85
95
 
86
96
  def full_screen(content)
87
-
88
97
  if !content
89
98
  return content
90
99
  end
@@ -1,3 +1,3 @@
1
1
  module Convert2Ascii
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -8,7 +8,7 @@ require_relative "./image2ascii"
8
8
  require_relative "./terminal-player"
9
9
  require_relative "./multi-tasker"
10
10
  require_relative "./check_package"
11
- require_relative './version'
11
+ require_relative "./version"
12
12
 
13
13
  module Convert2Ascii
14
14
  class Video2AsciiError < StandardError
@@ -17,14 +17,14 @@ module Convert2Ascii
17
17
  class Video2Ascii
18
18
  DEFAULT_STEP_DURATION = 0.04
19
19
 
20
-
21
20
  attr_accessor :uri, :width, :threads_count, :output, :step_duration
21
+
22
22
  def initialize(**args)
23
23
  @uri = args[:uri]
24
24
  @step_duration = args[:step_duration] || DEFAULT_STEP_DURATION
25
25
  @threads_count = set_threads_count
26
26
 
27
- @tmpdir = nil
27
+ @tmpdir = File.join(Dir.home, ".convert2ascii")
28
28
  @output = Dir.pwd
29
29
 
30
30
  # image2ascii attrs
@@ -34,7 +34,6 @@ module Convert2Ascii
34
34
  @color_block = args[:color_block] || false
35
35
 
36
36
  check_packages
37
- regist_hooks
38
37
  end
39
38
 
40
39
  def generate(**args)
@@ -43,34 +42,36 @@ module Convert2Ascii
43
42
  @color = args[:color] || @color # full
44
43
  @color_block = args[:color_block] || @color_block
45
44
 
46
- @tmpdir = Dir.mktmpdir
45
+ remove_tmpdir
46
+ Dir.mkdir(@tmpdir)
47
47
  @audio = get_audio_from_video(@tmpdir)
48
48
  screenshots_from_video(@tmpdir)
49
49
  convert_all_images(@tmpdir)
50
50
  @frames_path = order_frames_path
51
51
 
52
- self
53
- end
54
-
55
-
56
- def save(output_dir)
57
- system("rm -rf #{@tmpdir}/*.jpg")
58
- system("rm -rf #{output_dir} && mkdir #{output_dir}")
59
- system("cp -r #{@tmpdir}/* #{output_dir}")
60
-
61
52
  # save config
62
- File.open("#{output_dir}/meta.json", "w") do |f|
53
+ File.open("#{@tmpdir}/meta.json", "w") do |f|
63
54
  config = {
64
55
  step_duration: @step_duration,
65
- audio: @audio ? File.basename(@audio) : nil,
66
- frames_count: @frames_path.length
56
+ audio: @audio ? File.basename(@audio) : nil,
57
+ frames_count: @frames_path.length,
67
58
  }
68
59
  json_data = JSON.generate(config, pretty: true)
69
60
  f.puts json_data
70
61
  end
71
62
 
63
+ self
64
+ end
65
+
66
+ def save(output_dir)
67
+ system("rm -rf #{@tmpdir}/*.jpg")
68
+ system("rm -rf #{output_dir} && mkdir #{output_dir}")
69
+ system("cp -r #{@tmpdir}/* #{output_dir}")
70
+
72
71
  puts ""
73
72
  puts Rainbow("[info] save success!").green
73
+ ensure
74
+ after_clean
74
75
  end
75
76
 
76
77
  def order_frames_path
@@ -79,7 +80,7 @@ module Convert2Ascii
79
80
  get_name_order(a) <=> get_name_order(b)
80
81
  end
81
82
 
82
- @frames_path = frames_path
83
+ @frames_path = frames_path
83
84
  end
84
85
 
85
86
  def play(**args)
@@ -88,14 +89,21 @@ module Convert2Ascii
88
89
  frames = @frames_path.map { |f| File.open(f).read }
89
90
 
90
91
  player_args = {
91
- frames: ,
92
+ frames:,
92
93
  audio: @audio,
93
94
  play_loop:,
94
95
  step_duration:,
95
96
  }
97
+
96
98
  TerminalPlayer.new(**player_args).play
97
99
 
98
100
  return true
101
+ ensure
102
+ after_clean
103
+ end
104
+
105
+ def after_clean
106
+ remove_tmpdir
99
107
  end
100
108
 
101
109
  private
@@ -105,12 +113,10 @@ module Convert2Ascii
105
113
  CheckFFmpeg.new.check
106
114
  end
107
115
 
108
- def regist_hooks
109
- at_exit {
110
- if @tmpdir
111
- FileUtils.remove_entry @tmpdir
112
- end
113
- }
116
+ def remove_tmpdir
117
+ if File.directory? @tmpdir
118
+ FileUtils.remove_entry @tmpdir
119
+ end
114
120
  end
115
121
 
116
122
  def set_threads_count
@@ -156,7 +162,7 @@ module Convert2Ascii
156
162
  width: @width,
157
163
  style: @style,
158
164
  color: @color,
159
- color_block: @color_block
165
+ color_block: @color_block,
160
166
  }
161
167
  Image2Ascii.new(uri: image).generate(**config).ascii_string
162
168
  end
@@ -169,10 +175,7 @@ module Convert2Ascii
169
175
 
170
176
  def convert_all_images(save_dir)
171
177
  images = Dir.glob("#{save_dir}/*.jpg")
172
-
173
- processed_count = 0
174
178
  tasks = []
175
- time_start = Time.now
176
179
  images.each_with_index do |image, i|
177
180
  tasks << lambda {
178
181
  begin
@@ -181,9 +184,6 @@ module Convert2Ascii
181
184
  File.open("#{@tmpdir}/#{basename}.txt", "w") do |f|
182
185
  f.puts ascii_string
183
186
  end
184
-
185
- processed_count += 1
186
- print(Rainbow("\rprocessing... #{sprintf("%.2f", (1.0 * processed_count / images.length) * 100)} % (time: #{sprintf("%.2f", Time.now - time_start)} s)").green)
187
187
  rescue => error
188
188
  puts "-------"
189
189
  puts Rainbow("\n[Error] convert_to_ascii -> image: #{image} | i: #{i}").red
@@ -194,6 +194,5 @@ module Convert2Ascii
194
194
 
195
195
  MultiTasker.new(tasks).run
196
196
  end
197
-
198
197
  end
199
198
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: convert2ascii
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark24
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-01-12 00:00:00.000000000 Z
10
+ date: 2025-01-13 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rmagick
@@ -38,19 +38,19 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: 3.1.1
40
40
  - !ruby/object:Gem::Dependency
41
- name: async
41
+ name: parallel
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.21'
46
+ version: '1.26'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '2.21'
53
+ version: '1.26'
54
54
  description: This gem can help you convert image or video became ASCII art in your
55
55
  terminal.
56
56
  email: