kithe 2.16.0 → 2.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab5645f9d91b50b696549832f856cbf71513277a60061355fb8a0a222d60a3e9
4
- data.tar.gz: ed4b468d9b54650893feec779c392aaaaaff68b39d85e204dc0586f63235d381
3
+ metadata.gz: f3f74f2e5dc57d427f37635095e328d2dddd1498b86c85e153a126a8145308a5
4
+ data.tar.gz: 0ba6e1d1b075f3f3da6e9a6f697b354eb3b761dfb2c6e873e14e0e5e5a2782d3
5
5
  SHA512:
6
- metadata.gz: 4735e34556bad2004975cfef891eb87f597d3a2b7b32c6b8b3a154b1b363ba1c54a353481691215542ecfb02e3b63b212ae3a648e230ef83a1dba56da269285c
7
- data.tar.gz: 7b882426a155de5afb191af8accdfa36fa6194c768aff83759823e3d9ffb9d5d66c1a6974c575a91153f30e3797b6a8e9bec59500357a1fa3a460e00440be5c5
6
+ metadata.gz: 25fcab14c568fee5937de11be81fb534bfcc6329dba65c8d2da64739828af036a7890a497feba71d22ee0ca1b02f8ac270b4c6703d7270bade63b2d7cfb509ac
7
+ data.tar.gz: 9484fb022d18b0ab20203f368de6b7d65ec4871c57571c1f0b8a34733431bc8414b250ecb5eba3730435d27cf50e248ea27e56252d3e16c010fd5f3f936eeb32
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Kithe
2
2
  An experiment in shareable tools/components for building a digital collections app in Rails.
3
3
 
4
- [![Build Status](https://github.com/sciencehistory/kithe/workflows/CI/badge.svg?branch=master)](https://github.com/sciencehistory/kithe/actions?query=workflow%3ACI+branch%3Amaster) [![Gem Version](https://badge.fury.io/rb/kithe.svg)](https://badge.fury.io/rb/kithe)
4
+ [![CI](https://github.com/sciencehistory/kithe/actions/workflows/ci.yml/badge.svg)](https://github.com/sciencehistory/kithe/actions/workflows/ci.yml)
5
+ [![Gem Version](https://badge.fury.io/rb/kithe.svg)](https://badge.fury.io/rb/kithe)
5
6
 
6
7
  ## What is kithe?
7
8
 
@@ -8,6 +8,10 @@ module Kithe
8
8
  # @example tempfile = FfmpegExtractJpg.new.call(url)
9
9
  # @example tempfile = FfmpegExtractJpg.new(start_seconds: 60).call(shrine_uploaded_file)
10
10
  # @example tempfile = FfmpegExtractJpg.new(start_seconds: 10, width_pixels: 420).call(shrine_uploaded_file)
11
+ #
12
+ # @example you can also provide a Hash which will be mutated with metadata relevant to
13
+ # the derivative created, ffmpeg version and args:
14
+ # @example tempfile = FfmpegExtractJpg.new(start_seconds: 10, width_pixels: 420).call(shrine_uploaded_file, add_metadata: my_hash)
11
15
  class FfmpegExtractJpg
12
16
  class_attribute :ffmpeg_command, default: "ffmpeg"
13
17
  attr_reader :start_seconds, :frame_sample_size, :width_pixels
@@ -43,19 +47,19 @@ module Kithe
43
47
  # Most efficient is if we have a remote URL to give ffmpeg, one way or another!
44
48
  #
45
49
  # @returns [Tempfile] jpg extracted from movie
46
- def call(input_arg)
50
+ def call(input_arg, add_metadata:nil)
47
51
  if input_arg.kind_of?(Shrine::UploadedFile)
48
52
  if input_arg.respond_to?(:url) && input_arg.url&.start_with?(/https?\:/)
49
- _call(input_arg.url)
53
+ _call(input_arg.url, add_metadata: add_metadata)
50
54
  else
51
55
  Shrine.with_file(input_arg) do |local_file|
52
- _call(local_file.path)
56
+ _call(local_file.path, add_metadata: add_metadata)
53
57
  end
54
58
  end
55
59
  elsif input_arg.respond_to?(:path)
56
- _call(input_arg.path)
60
+ _call(input_arg.path, add_metadata: add_metadata)
57
61
  else
58
- _call(input_arg.to_s)
62
+ _call(input_arg.to_s, add_metadata: add_metadata)
59
63
  end
60
64
  end
61
65
 
@@ -67,14 +71,26 @@ module Kithe
67
71
  # @param ffmpeg_source_arg [String] filepath or URL. ffmpeg can take urls, which
68
72
  # can be very efficient.
69
73
  #
74
+ # @param add_metadata [Hash], optional, if provided will be filled out with metadata
75
+ # relevant to the derivative created -- ffmpeg version and args.
76
+ #
70
77
  # @returns Tempfile pointing to a thumbnail
71
- def _call(ffmpeg_source_arg)
78
+ def _call(ffmpeg_source_arg, add_metadata: nil)
72
79
  tempfile = Tempfile.new(['temp_deriv', ".jpg"])
73
80
 
74
81
  ffmpeg_args = produce_ffmpeg_args(input_arg: ffmpeg_source_arg, output_path: tempfile.path)
75
82
 
76
83
  TTY::Command.new(printer: :null).run(*ffmpeg_args)
77
84
 
85
+ if add_metadata
86
+ add_metadata[:ffmpeg_command] = ffmpeg_args.join(" ")
87
+
88
+ `#{ffmpeg_command} -version` =~ /ffmpeg version (\d+\.\d+.*) Copyright/
89
+ if $1
90
+ add_metadata[:ffmpeg_version] = $1
91
+ end
92
+ end
93
+
78
94
  return tempfile
79
95
  rescue StandardError => e
80
96
  tempfile.unlink if tempfile
@@ -39,13 +39,23 @@ module Kithe
39
39
  end
40
40
 
41
41
  # Will raise TTY::Command::ExitError if the ffmpeg returns non-null.
42
- def call(original_file)
42
+ def call(original_file, add_metadata: nil)
43
43
  tempfile = Tempfile.new(['temp_deriv', ".#{@output_suffix}"])
44
44
  # -y tells ffmpeg to overwrite the abovementioned tempfile (still empty)
45
45
  # with the output of ffmpeg.
46
46
  ffmpeg_args = [ffmpeg_command, "-y", "-i", original_file.path]
47
47
  ffmpeg_args += transform_arguments + [tempfile.path]
48
- TTY::Command.new(printer: :null).run(*ffmpeg_args)
48
+ out, err = TTY::Command.new(printer: :null).run(*ffmpeg_args)
49
+
50
+ if add_metadata
51
+ add_metadata[:ffmpeg_command] = ffmpeg_args.join(" ")
52
+
53
+ `#{ffmpeg_command} -version` =~ /ffmpeg version (\d+\.\d+.*) Copyright/
54
+ if $1
55
+ add_metadata[:ffmpeg_version] = $1
56
+ end
57
+ end
58
+
49
59
  return tempfile
50
60
  end
51
61
  end
@@ -19,8 +19,8 @@ module Kithe
19
19
  # built for use with kithe derivatives transformations, eg:
20
20
  #
21
21
  # class Asset < KitheAsset
22
- # define_derivative(thumb) do |original_file|
23
- # Kithe::VipsCliImageToJpeg.new(max_width: 100, thumbnail_mode: true).call(original_file)
22
+ # Attacher.define_derivative(:thumb) do |original_file, add_metadata:|
23
+ # Kithe::VipsCliImageToJpeg.new(max_width: 100, thumbnail_mode: true).call(original_file, add_metadata: add_metadata)
24
24
  # end
25
25
  # end
26
26
  #
@@ -28,8 +28,11 @@ module Kithe
28
28
  # about ruby memory leaks or the GIL. An alternative that uses vips ruby bindings
29
29
  # would also be possible, and might work well, but this is what for us is tried
30
30
  # and true.
31
+ #
32
+ # Some usage suggestions at https://www.libvips.org/API/current/Using-vipsthumbnail.html
31
33
  class VipsCliImageToJpeg
32
- class_attribute :srgb_profile_path, default: Kithe::Engine.root.join("lib", "vendor", "icc", "sRGB2014.icc").to_s
34
+ # vips has a built-in srgb profile, don't need our own anymore.
35
+ #class_attribute :srgb_profile_path, default: Kithe::Engine.root.join("lib", "vendor", "icc", "sRGB2014.icc").to_s
33
36
  class_attribute :vips_thumbnail_command, default: "vipsthumbnail"
34
37
  class_attribute :vips_command, default: "vips"
35
38
 
@@ -47,7 +50,7 @@ module Kithe
47
50
  end
48
51
 
49
52
  # Will raise TTY::Command::ExitError if the external Vips command returns non-null.
50
- def call(original_file)
53
+ def call(original_file, add_metadata: nil)
51
54
  tempfile = Tempfile.new(["kithe_vips_cli_image_to_jpeg", ".jpg"])
52
55
 
53
56
  vips_args = []
@@ -59,7 +62,7 @@ module Kithe
59
62
  # really huge one million pixels so it should not come into play, and
60
63
  # we're constraining proportionally by width.
61
64
  # https://github.com/jcupitt/libvips/issues/781
62
- vips_args.concat [vips_thumbnail_command, original_file.path]
65
+ vips_args.concat [vips_thumbnail_command, "--version", original_file.path]
63
66
  vips_args.concat maybe_profile_normalization_args
64
67
  vips_args.concat ["--size", "#{max_width}x65500"]
65
68
  vips_args.concat ["-o", "#{tempfile.path}#{vips_jpg_params}"]
@@ -72,7 +75,16 @@ module Kithe
72
75
  vips_args.concat ["#{tempfile.path}#{vips_jpg_params}"]
73
76
  end
74
77
 
75
- TTY::Command.new(printer: :null).run(*vips_args)
78
+ out, err = TTY::Command.new(printer: :null).run(*vips_args)
79
+
80
+ if add_metadata
81
+ add_metadata[:vips_command] = vips_args.join(" ")
82
+
83
+ out =~ /vips[ \-](\d+\.\d+\.\d+.*$)/
84
+ if $1
85
+ add_metadata[:vips_version] = $1
86
+ end
87
+ end
76
88
 
77
89
  return tempfile
78
90
  end
@@ -88,7 +100,7 @@ module Kithe
88
100
  def maybe_profile_normalization_args
89
101
  return [] unless thumbnail_mode?
90
102
 
91
- ["--eprofile", srgb_profile_path, "--delete"]
103
+ ["--export-profile", "srgb"]
92
104
  end
93
105
 
94
106
  # Params to add on to end of JPG output path, as in:
@@ -103,7 +115,7 @@ module Kithe
103
115
  # @returns [String]
104
116
  def vips_jpg_params
105
117
  if thumbnail_mode?
106
- "[Q=#{jpeg_q},interlace,optimize_coding,strip]"
118
+ "[Q=#{jpeg_q},interlace,optimize_coding,keep=none]"
107
119
  else
108
120
  # could be higher Q for downloads if we want, but we don't right now
109
121
  # We do avoid striping metadata, no 'strip' directive.
@@ -9,12 +9,32 @@ class Kithe::Asset::DerivativeDefinition
9
9
  @proc = proc
10
10
  end
11
11
 
12
+ # @return [Hash] add_metadata hash of metadata to add to derivative on storage
12
13
  def call(original_file:,attacher:)
14
+ add_metadata = {}
15
+ kwargs = {}
16
+
13
17
  if proc_accepts_keyword?(:attacher)
14
- proc.call(original_file, attacher: attacher)
18
+ kwargs[:attacher] = attacher
19
+ end
20
+
21
+ if proc_accepts_keyword?(:add_metadata)
22
+ kwargs[:add_metadata] = add_metadata
23
+ end
24
+
25
+ return_val = if kwargs.present?
26
+ proc.call(original_file, **kwargs)
15
27
  else
16
28
  proc.call(original_file)
17
29
  end
30
+
31
+ # Save in context to later write to actual stored derivative metadata
32
+ if add_metadata.present?
33
+ attacher.context[:add_metadata] ||= {}
34
+ attacher.context[:add_metadata][key] = add_metadata
35
+ end
36
+
37
+ return_val
18
38
  end
19
39
 
20
40
  # Do content-type restrictions defined for this definition match a given asset?
data/lib/kithe/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kithe
2
- VERSION = '2.16.0'
2
+ VERSION = '2.17.0'
3
3
  end
@@ -32,9 +32,13 @@ class Shrine
32
32
 
33
33
  module InstanceMethods
34
34
 
35
- # Override to fix "filename" metadata to be something reasonable, regardless
35
+ # 1. Override to fix "filename" metadata to be something reasonable, regardless
36
36
  # of what if anything was the filename of the IO being attached. shrine S3 will
37
- # insist on setting a default content-disposition with this filename.
37
+ # insist on setting a default content-disposition with this filename, so we
38
+ # can use that.
39
+ #
40
+ # 2. Set any specified derivative metadata on derivative. Specified add_metadata
41
+ # found in context.
38
42
  def extract_metadata(io, derivative:nil, **context)
39
43
  result = super
40
44
 
@@ -43,6 +47,15 @@ class Shrine
43
47
  result["filename"] = "#{context[:record].friendlier_id}_#{derivative}.#{extension}"
44
48
  end
45
49
 
50
+ # If derivative, add timestamp and any specified extra data
51
+ if derivative
52
+ result["created_at"] ||= Time.current.utc.iso8601.to_s
53
+
54
+ if (metadata = context.dig(:add_metadata, derivative)).present?
55
+ result.merge!(metadata.stringify_keys)
56
+ end
57
+ end
58
+
46
59
  result
47
60
  end
48
61
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kithe
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.16.0
4
+ version: 2.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Rochkind
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-25 00:00:00.000000000 Z
11
+ date: 2025-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails