stickyflag 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/README.md +5 -1
- data/lib/stickyflag/configuration.rb +10 -2
- data/lib/stickyflag/external_cmds.rb +3 -1
- data/lib/stickyflag/tag_factory.rb +32 -73
- data/lib/stickyflag/tags/c.rb +4 -0
- data/lib/stickyflag/tags/mkv.rb +204 -0
- data/lib/stickyflag/tags/mmd.rb +4 -0
- data/lib/stickyflag/tags/pdf.rb +21 -40
- data/lib/stickyflag/tags/png.rb +4 -0
- data/lib/stickyflag/tags/tex.rb +4 -0
- data/lib/stickyflag/version.rb +1 -1
- data/spec/stickyflag/external_cmds_spec.rb +7 -7
- data/spec/stickyflag/tag_factory_spec.rb +5 -64
- data/spec/stickyflag/tags/mkv_spec.rb +54 -0
- data/spec/support/examples/mkv_no_tags.mkv +0 -0
- data/spec/support/examples/mkv_with_tag.mkv +0 -0
- data/stickyflag.gemspec +1 -0
- metadata +25 -2
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
*file tagging for everyone*
|
5
5
|
|
6
|
+
[![Build Status](https://secure.travis-ci.org/cpence/stickyflag.png)](http://travis-ci.org/cpence/stickyflag) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/cpence/stickyflag)
|
7
|
+
|
6
8
|
## What does it do?
|
7
9
|
|
8
10
|
Filesystems are great. Most of our file organization tasks can be performed by arranging things in hierarchical directories. But every once in a while, you have an organization problem that can really only be resolved with categories that *cut across* these hierarchical structures. What if you want everything related to Project X, including its code, meeting notes about it, photos you took of it, and so on? The code is in your development directory, meeting notes in a meetings directory, and photos in an images directory.
|
@@ -22,11 +24,13 @@ StickyFlag can currently tag:
|
|
22
24
|
* MultiMarkdown files
|
23
25
|
* PDF files (with `pdftk` installed; see below)
|
24
26
|
* PNG files
|
27
|
+
* Matroska video files
|
25
28
|
* Source code (C/C++, TeX)
|
26
29
|
|
27
30
|
## What else does it need?
|
28
31
|
|
29
|
-
If you're going to tag PDF files, you need to install [pdftk,](http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/) which is available for
|
32
|
+
* If you're going to tag PDF files, you need to install [pdftk,](http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/) which is available for all platforms (licensed under the GPL).
|
33
|
+
* If you're going to tag MKV files, you need to install [mkvtoolnix,](http://www.bunkus.org/videotools/mkvtoolnix/) which is available for all platforms (licensed under the GPL).
|
30
34
|
|
31
35
|
## How do I get it?
|
32
36
|
|
@@ -8,6 +8,11 @@ module StickyFlag
|
|
8
8
|
DEFAULT_CONFIG = {
|
9
9
|
:have_pdftk => false,
|
10
10
|
:pdftk_path => '',
|
11
|
+
:have_mkvextract => false,
|
12
|
+
:mkvextract_path => '',
|
13
|
+
:have_mkvpropedit => false,
|
14
|
+
:mkvpropedit_path => '',
|
15
|
+
|
11
16
|
:root => ''
|
12
17
|
}
|
13
18
|
|
@@ -15,7 +20,7 @@ module StickyFlag
|
|
15
20
|
@configuration ||= DEFAULT_CONFIG.clone
|
16
21
|
|
17
22
|
unless @configuration.keys.include?(key.to_sym)
|
18
|
-
raise Thor::Error.new(
|
23
|
+
raise Thor::Error.new("ERROR: Invalid configuration key (#{key.to_s})")
|
19
24
|
end
|
20
25
|
|
21
26
|
@configuration[key.to_sym]
|
@@ -25,7 +30,7 @@ module StickyFlag
|
|
25
30
|
@configuration ||= DEFAULT_CONFIG.clone
|
26
31
|
|
27
32
|
unless @configuration.keys.include?(key.to_sym)
|
28
|
-
raise Thor::Error.new(
|
33
|
+
raise Thor::Error.new("ERROR: invalid configuration key (#{key.to_s})")
|
29
34
|
end
|
30
35
|
|
31
36
|
@configuration[key.to_sym] = value
|
@@ -51,6 +56,9 @@ module StickyFlag
|
|
51
56
|
file_name = config_path
|
52
57
|
if File.file? file_name
|
53
58
|
@configuration = YAML::load(File.open(file_name, 'r:UTF-8'))
|
59
|
+
|
60
|
+
# Merge with the default to pick up new keys
|
61
|
+
@configuration = DEFAULT_CONFIG.merge(@configuration)
|
54
62
|
end
|
55
63
|
end
|
56
64
|
|
@@ -3,7 +3,6 @@ require 'thor'
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'backports'
|
5
5
|
require 'stickyflag/tags/pdf'
|
6
|
-
require 'stickyflag/tags/pdf'
|
7
6
|
require 'stickyflag/tags/png'
|
8
7
|
require 'stickyflag/tags/tex'
|
9
8
|
require 'stickyflag/tags/c'
|
@@ -11,98 +10,58 @@ require 'stickyflag/tags/mmd'
|
|
11
10
|
|
12
11
|
module StickyFlag
|
13
12
|
module TagFactory
|
13
|
+
TAG_MODULES = StickyFlag::Tags.constants.map { |sym|
|
14
|
+
StickyFlag::Tags.const_get(sym)
|
15
|
+
}.select { |const|
|
16
|
+
const.is_a?(Module) && const.respond_to?(:extensions)
|
17
|
+
}
|
18
|
+
|
19
|
+
TAG_EXTENSIONS = TAG_MODULES.map { |mod| mod.extensions }.reduce(:|)
|
20
|
+
|
14
21
|
def available_tagging_extensions
|
15
|
-
|
16
|
-
'.h', '.hpp', '.hxx', '.mmd']
|
22
|
+
TAG_EXTENSIONS
|
17
23
|
end
|
18
|
-
|
19
|
-
def
|
24
|
+
|
25
|
+
def call_tag_method(file_name, method, *args)
|
20
26
|
extension = File.extname file_name
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
when '.hxx' then return StickyFlag::Tags::C::get(file_name)
|
32
|
-
when '.mmd' then return StickyFlag::Tags::MMD::get(file_name)
|
33
|
-
else raise Thor::Error.new("ERROR: Don't know how to get tags for a file of extension '#{extension}'")
|
27
|
+
|
28
|
+
TAG_MODULES.each do |mod|
|
29
|
+
if mod.send(:extensions).include? extension
|
30
|
+
if mod.respond_to?(:config_values)
|
31
|
+
config_values = mod.send(:config_values)
|
32
|
+
config_values.each { |val| args << get_config(val) }
|
33
|
+
end
|
34
|
+
|
35
|
+
return mod.send(method, file_name, *args)
|
36
|
+
end
|
34
37
|
end
|
38
|
+
|
39
|
+
raise Thor::Error.new("ERROR: Don't know how to tag a file of extension '#{extension}'")
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_tags_for(file_name)
|
43
|
+
call_tag_method(file_name, :get)
|
35
44
|
end
|
36
45
|
|
37
46
|
def set_tag_for(file_name, tag)
|
38
47
|
tags = get_tags_for file_name
|
39
|
-
if tags.include? tag
|
40
|
-
# Already set
|
41
|
-
return
|
42
|
-
end
|
48
|
+
return if tags.include? tag
|
43
49
|
|
44
50
|
set_database_tag file_name, tag
|
45
|
-
|
46
|
-
extension = File.extname file_name
|
47
|
-
case extension
|
48
|
-
when '.pdf' then return StickyFlag::Tags::PDF::set(file_name, tag, get_config(:pdftk_path))
|
49
|
-
when '.png' then return StickyFlag::Tags::PNG::set(file_name, tag)
|
50
|
-
when '.tex' then return StickyFlag::Tags::TeX::set(file_name, tag)
|
51
|
-
when '.c' then return StickyFlag::Tags::C::set(file_name, tag)
|
52
|
-
when '.cpp' then return StickyFlag::Tags::C::set(file_name, tag)
|
53
|
-
when '.cxx' then return StickyFlag::Tags::C::set(file_name, tag)
|
54
|
-
when '.c++' then return StickyFlag::Tags::C::set(file_name, tag)
|
55
|
-
when '.h' then return StickyFlag::Tags::C::set(file_name, tag)
|
56
|
-
when '.hpp' then return StickyFlag::Tags::C::set(file_name, tag)
|
57
|
-
when '.hxx' then return StickyFlag::Tags::C::set(file_name, tag)
|
58
|
-
when '.mmd' then return StickyFlag::Tags::MMD::set(file_name, tag)
|
59
|
-
else raise Thor::Error.new("ERROR: Don't know how to set tags for a file of extension '#{extension}'")
|
60
|
-
end
|
51
|
+
call_tag_method(file_name, :set, tag)
|
61
52
|
end
|
62
53
|
|
63
54
|
def unset_tag_for(file_name, tag)
|
64
55
|
tags = get_tags_for file_name
|
65
|
-
unless tags.include? tag
|
66
|
-
raise Thor::Error.new("ERROR: Cannot unset tag #{tag} from file, not set")
|
67
|
-
end
|
56
|
+
raise Thor::Error.new("ERROR: Cannot unset tag #{tag} from file, not set") unless tags.include? tag
|
68
57
|
|
69
58
|
unset_database_tag file_name, tag
|
70
|
-
|
71
|
-
extension = File.extname file_name
|
72
|
-
case extension
|
73
|
-
when '.pdf' then return StickyFlag::Tags::PDF::unset(file_name, tag, get_config(:pdftk_path))
|
74
|
-
when '.png' then return StickyFlag::Tags::PNG::unset(file_name, tag)
|
75
|
-
when '.tex' then return StickyFlag::Tags::TeX::unset(file_name, tag)
|
76
|
-
when '.c' then return StickyFlag::Tags::C::unset(file_name, tag)
|
77
|
-
when '.cpp' then return StickyFlag::Tags::C::unset(file_name, tag)
|
78
|
-
when '.cxx' then return StickyFlag::Tags::C::unset(file_name, tag)
|
79
|
-
when '.c++' then return StickyFlag::Tags::C::unset(file_name, tag)
|
80
|
-
when '.h' then return StickyFlag::Tags::C::unset(file_name, tag)
|
81
|
-
when '.hpp' then return StickyFlag::Tags::C::unset(file_name, tag)
|
82
|
-
when '.hxx' then return StickyFlag::Tags::C::unset(file_name, tag)
|
83
|
-
when '.mmd' then return StickyFlag::Tags::MMD::unset(file_name, tag)
|
84
|
-
else raise Thor::Error.new("ERROR: Don't know how to unset tags for a file of extension '#{extension}'")
|
85
|
-
end
|
59
|
+
call_tag_method(file_name, :unset, tag)
|
86
60
|
end
|
87
61
|
|
88
62
|
def clear_tags_for(file_name)
|
89
63
|
clear_database_tags file_name
|
90
|
-
|
91
|
-
extension = File.extname file_name
|
92
|
-
case extension
|
93
|
-
when '.pdf' then return StickyFlag::Tags::PDF::clear(file_name, get_config(:pdftk_path))
|
94
|
-
when '.png' then return StickyFlag::Tags::PNG::clear(file_name)
|
95
|
-
when '.tex' then return StickyFlag::Tags::TeX::clear(file_name)
|
96
|
-
when '.c' then return StickyFlag::Tags::C::clear(file_name)
|
97
|
-
when '.cpp' then return StickyFlag::Tags::C::clear(file_name)
|
98
|
-
when '.cxx' then return StickyFlag::Tags::C::clear(file_name)
|
99
|
-
when '.c++' then return StickyFlag::Tags::C::clear(file_name)
|
100
|
-
when '.h' then return StickyFlag::Tags::C::clear(file_name)
|
101
|
-
when '.hpp' then return StickyFlag::Tags::C::clear(file_name)
|
102
|
-
when '.hxx' then return StickyFlag::Tags::C::clear(file_name)
|
103
|
-
when '.mmd' then return StickyFlag::Tags::MMD::clear(file_name)
|
104
|
-
else raise Thor::Error.new("ERROR: Don't know how to clear all tags for a file of extension '#{extension}'")
|
105
|
-
end
|
64
|
+
call_tag_method(file_name, :clear)
|
106
65
|
end
|
107
66
|
end
|
108
67
|
end
|
data/lib/stickyflag/tags/c.rb
CHANGED
@@ -0,0 +1,204 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'thor'
|
3
|
+
require 'open3'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'stickyflag/patches/tempfile_encoding'
|
6
|
+
|
7
|
+
module StickyFlag
|
8
|
+
module Tags
|
9
|
+
module MKV
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def extensions
|
13
|
+
[ '.mkv' ]
|
14
|
+
end
|
15
|
+
def config_values
|
16
|
+
[ :mkvextract_path, :mkvpropedit_path ]
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_tag_xml(file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
|
20
|
+
stdout_str = ''
|
21
|
+
stderr_str = ''
|
22
|
+
|
23
|
+
begin
|
24
|
+
Open3.popen3("#{mkvextract_path} tags #{file_name} -q") do |i, o, e, t|
|
25
|
+
out_reader = Thread.new { o.read }
|
26
|
+
err_reader = Thread.new { e.read }
|
27
|
+
i.close
|
28
|
+
stdout_str = out_reader.value
|
29
|
+
stderr_str = err_reader.value
|
30
|
+
|
31
|
+
stdout_str.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
|
32
|
+
stderr_str.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
|
33
|
+
end
|
34
|
+
rescue Exception
|
35
|
+
raise Thor::Error.new("ERROR: Failed to get tags for #{file_name}; mkvextract call failed")
|
36
|
+
end
|
37
|
+
if stderr_str.start_with?("Error: ") || stderr_str.include?("Errno::ENOENT") || stdout_str.start_with?("Error: ")
|
38
|
+
raise Thor::Error.new("ERROR: Failed to get tags for #{file_name}; mkvextract call failed")
|
39
|
+
end
|
40
|
+
|
41
|
+
if stdout_str == ''
|
42
|
+
# This is what happens when a file has no tags whatsoever, we need
|
43
|
+
# to build a skeleton document
|
44
|
+
stdout_str = <<-XML
|
45
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
46
|
+
|
47
|
+
<!DOCTYPE Tags SYSTEM "matroskatags.dtd">
|
48
|
+
|
49
|
+
<Tags>
|
50
|
+
<Tag>
|
51
|
+
<Targets>
|
52
|
+
</Targets>
|
53
|
+
</Tag>
|
54
|
+
</Tags>
|
55
|
+
XML
|
56
|
+
end
|
57
|
+
|
58
|
+
# Strip off newlines and BOM, these wreak havoc on the Java XML parser,
|
59
|
+
# which considers them content before the prolog
|
60
|
+
bom = "\xEF\xBB\xBF"
|
61
|
+
bom.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
|
62
|
+
stdout_str.strip!.gsub!(bom, '')
|
63
|
+
|
64
|
+
Nokogiri::XML(stdout_str)
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_stickyflag_root(xml_doc)
|
68
|
+
xml_doc.xpath("/Tags/Tag").each do |tag|
|
69
|
+
targets = tag.at_xpath("Targets")
|
70
|
+
next if targets.nil? # This shouldn't ever happen
|
71
|
+
|
72
|
+
# If there's no UID elements under this tag, then we're good (note
|
73
|
+
# that sometimes mkvpropedit will *force* in a TargetTypeValue or
|
74
|
+
# a TargetType, and we don't want to block on those)
|
75
|
+
good = true
|
76
|
+
targets.children.each do |child|
|
77
|
+
if child.name.end_with? 'UID'
|
78
|
+
good = false
|
79
|
+
break
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return tag if good
|
84
|
+
end
|
85
|
+
|
86
|
+
# Make one, then
|
87
|
+
tag_tag = Nokogiri::XML::Node.new 'Tag', xml_doc
|
88
|
+
targets_tag = Nokogiri::XML::Node.new 'Targets', xml_doc
|
89
|
+
tag_tag.add_child targets_tag
|
90
|
+
|
91
|
+
root_elem = xml_doc.at_xpath("/Tags")
|
92
|
+
root_elem.add_child tag_tag
|
93
|
+
|
94
|
+
tag_tag
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_stickyflag_tag(xml_doc)
|
98
|
+
tag_tag = get_stickyflag_root(xml_doc)
|
99
|
+
if tag_tag.nil?
|
100
|
+
# Should never happen!
|
101
|
+
raise Thor::Error.new("INTERNAL ERROR: Failed to find the StickyFlag tag root in MKV XML")
|
102
|
+
end
|
103
|
+
|
104
|
+
tag_tag.at_xpath("Simple[Name = 'X_STICKYFLAG_FLAGS']/String")
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_tag_xml(xml_doc, file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
|
108
|
+
# Write out this XML file and attach it to the MKV
|
109
|
+
outfile = Tempfile.new_with_encoding ['sfmkvtag', '.xml']
|
110
|
+
begin
|
111
|
+
outfile.write(xml_doc.to_xml)
|
112
|
+
outfile.close
|
113
|
+
|
114
|
+
ret = system(mkvpropedit_path, file_name, '--tags', "all:#{outfile.path}", "-q")
|
115
|
+
unless ret == true
|
116
|
+
raise Thor::Error.new("ERROR: Failed to update tag for #{file_name}; mkvpropedit call failed")
|
117
|
+
end
|
118
|
+
ensure
|
119
|
+
outfile.unlink
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def get(file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
|
124
|
+
xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
|
125
|
+
stickyflag_tag = get_stickyflag_tag(xml_doc)
|
126
|
+
return [] if stickyflag_tag.nil?
|
127
|
+
|
128
|
+
tag_string = stickyflag_tag.content
|
129
|
+
return [] if tag_string.nil? || tag_string.empty?
|
130
|
+
|
131
|
+
tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
|
132
|
+
end
|
133
|
+
|
134
|
+
def set(file_name, tag, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
|
135
|
+
xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
|
136
|
+
stickyflag_tag = get_stickyflag_tag(xml_doc)
|
137
|
+
|
138
|
+
unless stickyflag_tag.nil?
|
139
|
+
# We already have the right tag, check it
|
140
|
+
tag_string = stickyflag_tag.content
|
141
|
+
unless tag_string.nil? || tag_string.empty?
|
142
|
+
tags = tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
|
143
|
+
return if tags.include? tag
|
144
|
+
tags << tag
|
145
|
+
|
146
|
+
# Set the new string in the XML file
|
147
|
+
new_tag_string = tags.join(', ')
|
148
|
+
stickyflag_tag.content = new_tag_string
|
149
|
+
else
|
150
|
+
# Somehow the tag has no content, write it in
|
151
|
+
stickyflag_tag.content = tag
|
152
|
+
end
|
153
|
+
else
|
154
|
+
# No tag, add it
|
155
|
+
root = get_stickyflag_root(xml_doc)
|
156
|
+
simple_tag = Nokogiri::XML::Node.new 'Simple', xml_doc
|
157
|
+
|
158
|
+
name_tag = Nokogiri::XML::Node.new 'Name', xml_doc
|
159
|
+
name_tag.content = 'X_STICKYFLAG_FLAGS'
|
160
|
+
string_tag = Nokogiri::XML::Node.new 'String', xml_doc
|
161
|
+
string_tag.content = tag
|
162
|
+
|
163
|
+
simple_tag.add_child(name_tag)
|
164
|
+
simple_tag.add_child(string_tag)
|
165
|
+
|
166
|
+
root.add_child(simple_tag)
|
167
|
+
end
|
168
|
+
|
169
|
+
set_tag_xml(xml_doc, file_name, mkvextract_path, mkvpropedit_path)
|
170
|
+
end
|
171
|
+
|
172
|
+
def unset(file_name, tag, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
|
173
|
+
xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
|
174
|
+
stickyflag_tag = get_stickyflag_tag(xml_doc)
|
175
|
+
|
176
|
+
unless stickyflag_tag.nil?
|
177
|
+
# We already have the right tag, check it
|
178
|
+
tag_string = stickyflag_tag.content
|
179
|
+
unless tag_string.nil? || tag_string.empty?
|
180
|
+
tags = tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
|
181
|
+
return unless tags.include? tag
|
182
|
+
tags.delete(tag)
|
183
|
+
|
184
|
+
# Set the new string in the XML file
|
185
|
+
new_tag_string = tags.join(', ')
|
186
|
+
stickyflag_tag.content = new_tag_string
|
187
|
+
|
188
|
+
set_tag_xml(xml_doc, file_name, mkvextract_path, mkvpropedit_path)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def clear(file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
|
194
|
+
xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
|
195
|
+
stickyflag_tag = get_stickyflag_tag(xml_doc)
|
196
|
+
|
197
|
+
unless stickyflag_tag.nil?
|
198
|
+
stickyflag_tag.remove
|
199
|
+
set_tag_xml(xml_doc, file_name, mkvextract_path, mkvpropedit_path)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
data/lib/stickyflag/tags/mmd.rb
CHANGED
@@ -5,6 +5,10 @@ module StickyFlag
|
|
5
5
|
module Tags
|
6
6
|
module MMD
|
7
7
|
module_function
|
8
|
+
|
9
|
+
def extensions
|
10
|
+
[ '.md', '.mmd', '.markdown', '.text' ]
|
11
|
+
end
|
8
12
|
|
9
13
|
# We duplicate the source-code module here because we want to have
|
10
14
|
# cute support for the indentation of tag contents in MMD, and because
|
data/lib/stickyflag/tags/pdf.rb
CHANGED
@@ -7,6 +7,13 @@ module StickyFlag
|
|
7
7
|
module Tags
|
8
8
|
module PDF
|
9
9
|
module_function
|
10
|
+
|
11
|
+
def extensions
|
12
|
+
[ '.pdf' ]
|
13
|
+
end
|
14
|
+
def config_values
|
15
|
+
[ :pdftk_path ]
|
16
|
+
end
|
10
17
|
|
11
18
|
def get(file_name, pdftk_path = 'pdftk')
|
12
19
|
stdout_str = ''
|
@@ -47,13 +54,8 @@ module StickyFlag
|
|
47
54
|
|
48
55
|
tags
|
49
56
|
end
|
50
|
-
|
51
|
-
def set(file_name, tag, pdftk_path = 'pdftk')
|
52
|
-
tags = get(file_name, pdftk_path)
|
53
|
-
return if tags.include? tag
|
54
|
-
|
55
|
-
tags << tag
|
56
57
|
|
58
|
+
def write_tags_to(file_name, tags, pdftk_path = 'pdftk')
|
57
59
|
info = Tempfile.new_with_encoding ['sfpdftag', '.txt']
|
58
60
|
begin
|
59
61
|
info.write("InfoKey: X-StickyFlag-Flags\n")
|
@@ -61,9 +63,9 @@ module StickyFlag
|
|
61
63
|
info.close
|
62
64
|
|
63
65
|
outpath = File.tmpnam('.pdf')
|
64
|
-
ret = system(pdftk_path, file_name
|
66
|
+
ret = system(pdftk_path, file_name, 'update_info', info.path, 'output', outpath)
|
65
67
|
unless ret == true
|
66
|
-
raise Thor::Error.new("ERROR: Failed to
|
68
|
+
raise Thor::Error.new("ERROR: Failed to write tag for #{file_name}; pdftk call failed")
|
67
69
|
end
|
68
70
|
|
69
71
|
FileUtils.mv outpath, file_name
|
@@ -71,48 +73,27 @@ module StickyFlag
|
|
71
73
|
info.unlink
|
72
74
|
end
|
73
75
|
end
|
76
|
+
|
77
|
+
def set(file_name, tag, pdftk_path = 'pdftk')
|
78
|
+
tags = get(file_name, pdftk_path)
|
79
|
+
return if tags.include? tag
|
80
|
+
|
81
|
+
tags << tag
|
82
|
+
|
83
|
+
write_tags_to(file_name, tags, pdftk_path)
|
84
|
+
end
|
74
85
|
|
75
86
|
def unset(file_name, tag, pdftk_path = 'pdftk')
|
76
87
|
tags = get(file_name, pdftk_path)
|
77
88
|
return unless tags.include? tag
|
78
89
|
|
79
90
|
tags.delete(tag)
|
80
|
-
|
81
|
-
info = Tempfile.new_with_encoding ['sfpdftag', '.txt']
|
82
|
-
begin
|
83
|
-
info.write("InfoKey: X-StickyFlag-Flags\n")
|
84
|
-
info.write("InfoValue: #{tags.join(', ')}\n")
|
85
|
-
info.close
|
86
91
|
|
87
|
-
|
88
|
-
ret = system(pdftk_path, file_name.to_s, 'update_info', info.path, 'output', outpath)
|
89
|
-
unless ret == true
|
90
|
-
raise Thor::Error.new("ERROR: Failed to unset tag for #{file_name}; pdftk call failed")
|
91
|
-
end
|
92
|
-
|
93
|
-
FileUtils.mv outpath, file_name
|
94
|
-
ensure
|
95
|
-
info.unlink
|
96
|
-
end
|
92
|
+
write_tags_to(file_name, tags, pdftk_path)
|
97
93
|
end
|
98
94
|
|
99
95
|
def clear(file_name, pdftk_path = 'pdftk')
|
100
|
-
|
101
|
-
begin
|
102
|
-
info.write("InfoKey: X-StickyFlag-Flags\n")
|
103
|
-
info.write("InfoValue: \n")
|
104
|
-
info.close
|
105
|
-
|
106
|
-
outpath = File.tmpnam('.pdf')
|
107
|
-
ret = system(pdftk_path, file_name.to_s, 'update_info', info.path, 'output', outpath)
|
108
|
-
unless ret == true
|
109
|
-
raise Thor::Error.new("ERROR: Failed to clear tags for #{file_name}; pdftk call failed")
|
110
|
-
end
|
111
|
-
|
112
|
-
FileUtils.mv outpath, file_name
|
113
|
-
ensure
|
114
|
-
info.unlink
|
115
|
-
end
|
96
|
+
write_tags_to(file_name, [], pdftk_path)
|
116
97
|
end
|
117
98
|
end
|
118
99
|
end
|
data/lib/stickyflag/tags/png.rb
CHANGED
data/lib/stickyflag/tags/tex.rb
CHANGED
data/lib/stickyflag/version.rb
CHANGED
@@ -51,7 +51,7 @@ describe 'StickyFlag::ExternalCmds' do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'outputs a warning message' do
|
54
|
-
@obj.should_receive(:say_status).with(:warning, kind_of(String), kind_of(Symbol))
|
54
|
+
@obj.should_receive(:say_status).with(:warning, kind_of(String), kind_of(Symbol)).at_least(:once)
|
55
55
|
@obj.find_external_cmds
|
56
56
|
end
|
57
57
|
end
|
@@ -81,8 +81,8 @@ describe 'StickyFlag::ExternalCmds' do
|
|
81
81
|
end
|
82
82
|
|
83
83
|
it 'should not print any messages' do
|
84
|
-
@obj.should_not_receive(:say)
|
85
|
-
@obj.should_not_receive(:say_status)
|
84
|
+
@obj.should_not_receive(:say).with(/pdftk/)
|
85
|
+
@obj.should_not_receive(:say_status).with(:warning, /pdftk/, kind_of(Symbol))
|
86
86
|
@obj.find_external_cmds
|
87
87
|
end
|
88
88
|
end
|
@@ -109,7 +109,7 @@ describe 'StickyFlag::ExternalCmds' do
|
|
109
109
|
|
110
110
|
it 'prints out an error message' do
|
111
111
|
@obj.stub(:say_status) {}
|
112
|
-
@obj.should_receive(:say_status).with(:error,
|
112
|
+
@obj.should_receive(:say_status).with(:error, /pdftk/, kind_of(Symbol))
|
113
113
|
@obj.find_external_cmds
|
114
114
|
end
|
115
115
|
end
|
@@ -136,7 +136,7 @@ describe 'StickyFlag::ExternalCmds' do
|
|
136
136
|
|
137
137
|
it 'prints out an error message' do
|
138
138
|
@obj.stub(:say_status) {}
|
139
|
-
@obj.should_receive(:say_status).with(:error,
|
139
|
+
@obj.should_receive(:say_status).with(:error, /pdftk/, kind_of(Symbol))
|
140
140
|
@obj.find_external_cmds
|
141
141
|
end
|
142
142
|
end
|
@@ -166,8 +166,8 @@ describe 'StickyFlag::ExternalCmds' do
|
|
166
166
|
end
|
167
167
|
|
168
168
|
it 'should not print any messages' do
|
169
|
-
@obj.should_not_receive(:say)
|
170
|
-
@obj.should_not_receive(:say_status)
|
169
|
+
@obj.should_not_receive(:say).with(/pdftk/)
|
170
|
+
@obj.should_not_receive(:say_status).with(:warning, /pdftk/, kind_of(Symbol))
|
171
171
|
@obj.find_external_cmds
|
172
172
|
end
|
173
173
|
end
|
@@ -33,70 +33,11 @@ describe 'StickyFlag::TagFactory' do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
describe '.available_tagging_extensions' do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
(@obj.available_tagging_extensions - CANNOT_TEST).each do |ext|
|
42
|
-
file = Tempfile.new_with_encoding(['ext', ext])
|
43
|
-
file.puts('test')
|
44
|
-
file.close
|
45
|
-
|
46
|
-
expect { @obj.get_tags_for(file.path) }.to_not raise_error
|
47
|
-
file.unlink
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'should call get for other extensions' do
|
52
|
-
CANNOT_TEST.each do |ext|
|
53
|
-
path = copy_example "#{ext[1..-1]}_with_tag#{ext}"
|
54
|
-
expect { @obj.clear_tags_for(path) }.to_not raise_error
|
55
|
-
File.unlink(path)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'should call set/unset for every good extension' do
|
60
|
-
(@obj.available_tagging_extensions - CANNOT_TEST).each do |ext|
|
61
|
-
file = Tempfile.new_with_encoding(['ext', ext])
|
62
|
-
file.puts('test')
|
63
|
-
file.close
|
64
|
-
|
65
|
-
expect {
|
66
|
-
@obj.set_tag_for(file.path, 'test')
|
67
|
-
@obj.unset_tag_for(file.path, 'test')
|
68
|
-
}.to_not raise_error
|
69
|
-
file.unlink
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'should call set/unset for other extensions' do
|
74
|
-
CANNOT_TEST.each do |ext|
|
75
|
-
path = copy_example "#{ext[1..-1]}_with_tag#{ext}"
|
76
|
-
expect {
|
77
|
-
@obj.set_tag_for(path, 'test2')
|
78
|
-
@obj.unset_tag_for(path, 'test2')
|
79
|
-
}.to_not raise_error
|
80
|
-
File.unlink(path)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'should call clear for every good extension' do
|
85
|
-
(@obj.available_tagging_extensions - CANNOT_TEST).each do |ext|
|
86
|
-
file = Tempfile.new_with_encoding(['ext', ext])
|
87
|
-
file.puts('test')
|
88
|
-
file.close
|
89
|
-
|
90
|
-
expect { @obj.clear_tags_for(file.path) }.to_not raise_error
|
91
|
-
file.unlink
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'should call clear for other extensions' do
|
96
|
-
CANNOT_TEST.each do |ext|
|
97
|
-
path = copy_example "#{ext[1..-1]}_with_tag#{ext}"
|
98
|
-
expect { @obj.clear_tags_for(path) }.to_not raise_error
|
99
|
-
File.unlink(path)
|
36
|
+
it 'should be able to call through to a class method for every good extension' do
|
37
|
+
@obj.available_tagging_extensions.each do |ext|
|
38
|
+
# This is a bit of a hack -- call TagClass::respond_to?, which takes
|
39
|
+
# one argument
|
40
|
+
expect { @obj.call_tag_method("test#{ext}", :respond_to?) }.to_not raise_error
|
100
41
|
end
|
101
42
|
end
|
102
43
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'stickyflag/tags/mkv'
|
3
|
+
require 'stickyflag/paths'
|
4
|
+
require 'stickyflag/configuration'
|
5
|
+
require 'stickyflag/external_cmds'
|
6
|
+
|
7
|
+
class GetConfiguration
|
8
|
+
include StickyFlag::Paths
|
9
|
+
include StickyFlag::Configuration
|
10
|
+
include StickyFlag::ExternalCmds
|
11
|
+
end
|
12
|
+
|
13
|
+
describe StickyFlag::Tags::MKV do
|
14
|
+
before(:each) do
|
15
|
+
config = GetConfiguration.new
|
16
|
+
config.stub(:load_config!) { }
|
17
|
+
config.stub(:save_config!) { }
|
18
|
+
|
19
|
+
config.find_external_cmds
|
20
|
+
|
21
|
+
@mkve = config.get_config(:mkvextract_path)
|
22
|
+
@mkvp = config.get_config(:mkvpropedit_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
it_behaves_like 'a tag handler' do
|
26
|
+
let(:params) {
|
27
|
+
[ @mkve, @mkvp ]
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when we set our own tag' do
|
32
|
+
it "doesn't wipe out other tags set on the file" do
|
33
|
+
path = copy_example("mkv_with_tag.mkv")
|
34
|
+
StickyFlag::Tags::MKV.set(path, 'test2', @mkve, @mkvp)
|
35
|
+
|
36
|
+
# This is an internal method, but it's too useful not to use
|
37
|
+
xml_doc = StickyFlag::Tags::MKV.get_tag_xml(path, @mkve, @mkvp)
|
38
|
+
|
39
|
+
# This is one of the original tags on the file
|
40
|
+
tag = xml_doc.at_xpath("/Tags/Tag[Targets[TargetTypeValue = '50']]/Simple[Name = 'TITLE']")
|
41
|
+
tag.should_not be_nil
|
42
|
+
tag.at_xpath("String").content.should eq('Big Buck Bunny - test 1')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with bad mkv utility paths' do
|
47
|
+
it 'raises errors for everything' do
|
48
|
+
expect { StickyFlag::Tags::MKV.get(example_path("mkv_with_tag.mkv"), '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
|
49
|
+
expect { StickyFlag::Tags::MKV.clear(example_path("mkv_with_tag.mkv"), '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
|
50
|
+
expect { StickyFlag::Tags::MKV.set(example_path("mkv_with_tag.mkv"), 'test2', '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
|
51
|
+
expect { StickyFlag::Tags::MKV.unset(example_path("mkv_with_tag.mkv"), 'test', '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
Binary file
|
Binary file
|
data/stickyflag.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stickyflag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: nokogiri
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
79
|
name: chunky_png
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -282,6 +298,7 @@ files:
|
|
282
298
|
- lib/stickyflag/paths.rb
|
283
299
|
- lib/stickyflag/tag_factory.rb
|
284
300
|
- lib/stickyflag/tags/c.rb
|
301
|
+
- lib/stickyflag/tags/mkv.rb
|
285
302
|
- lib/stickyflag/tags/mmd.rb
|
286
303
|
- lib/stickyflag/tags/pdf.rb
|
287
304
|
- lib/stickyflag/tags/png.rb
|
@@ -297,6 +314,7 @@ files:
|
|
297
314
|
- spec/stickyflag/paths_spec.rb
|
298
315
|
- spec/stickyflag/tag_factory_spec.rb
|
299
316
|
- spec/stickyflag/tags/c_spec.rb
|
317
|
+
- spec/stickyflag/tags/mkv_spec.rb
|
300
318
|
- spec/stickyflag/tags/mmd_spec.rb
|
301
319
|
- spec/stickyflag/tags/pdf_spec.rb
|
302
320
|
- spec/stickyflag/tags/png_spec.rb
|
@@ -306,6 +324,8 @@ files:
|
|
306
324
|
- spec/support/examples/c_all_comments.c
|
307
325
|
- spec/support/examples/c_no_tags.c
|
308
326
|
- spec/support/examples/c_with_tag.c
|
327
|
+
- spec/support/examples/mkv_no_tags.mkv
|
328
|
+
- spec/support/examples/mkv_with_tag.mkv
|
309
329
|
- spec/support/examples/mmd_all_meta.mmd
|
310
330
|
- spec/support/examples/mmd_crazy_keys.mmd
|
311
331
|
- spec/support/examples/mmd_crazy_tags.mmd
|
@@ -373,6 +393,7 @@ test_files:
|
|
373
393
|
- spec/stickyflag/paths_spec.rb
|
374
394
|
- spec/stickyflag/tag_factory_spec.rb
|
375
395
|
- spec/stickyflag/tags/c_spec.rb
|
396
|
+
- spec/stickyflag/tags/mkv_spec.rb
|
376
397
|
- spec/stickyflag/tags/mmd_spec.rb
|
377
398
|
- spec/stickyflag/tags/pdf_spec.rb
|
378
399
|
- spec/stickyflag/tags/png_spec.rb
|
@@ -382,6 +403,8 @@ test_files:
|
|
382
403
|
- spec/support/examples/c_all_comments.c
|
383
404
|
- spec/support/examples/c_no_tags.c
|
384
405
|
- spec/support/examples/c_with_tag.c
|
406
|
+
- spec/support/examples/mkv_no_tags.mkv
|
407
|
+
- spec/support/examples/mkv_with_tag.mkv
|
385
408
|
- spec/support/examples/mmd_all_meta.mmd
|
386
409
|
- spec/support/examples/mmd_crazy_keys.mmd
|
387
410
|
- spec/support/examples/mmd_crazy_tags.mmd
|