ZMediumToMarkdown 2.3.8 → 2.4.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 +4 -4
- data/bin/ZMediumToMarkdown +74 -37
- data/lib/Helper.rb +17 -1
- data/lib/Post.rb +15 -2
- data/lib/ZMediumFetcher.rb +36 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8a5ca887a7781219ae534d50ebe022c796d21ddc6eb5fab977559fd65ce4cb2
|
4
|
+
data.tar.gz: 4e43067b4502adc9a11cf3347c68091980c273e567d22811e6d4d02b233ef5a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e7ecbc20c8038097d213bfce93f6129b8451f7a990ad97c04f07533b6b9c0be8c0a913d4599d19b8aa6ca8e8d95724da4190f6f55c5678c5e944078d022a390
|
7
|
+
data.tar.gz: 7ea56ec01d108d51a1f56087e28a5596a7e865dbc937507ccf2e029ed8ac1f536225a940437d30a5e36ab3a74e7c1fef38b410f0adc7b6081144baafdcbd3e4b
|
data/bin/ZMediumToMarkdown
CHANGED
@@ -8,69 +8,106 @@ require "ZMediumFetcher"
|
|
8
8
|
require "Helper"
|
9
9
|
require "optparse"
|
10
10
|
|
11
|
+
$cookie_sid = nil
|
12
|
+
$cookie_uid = nil
|
13
|
+
|
11
14
|
class Main
|
12
15
|
def initialize
|
13
16
|
fetcher = ZMediumFetcher.new
|
14
17
|
ARGV << '-h' if ARGV.empty?
|
15
18
|
|
19
|
+
options = {}
|
16
20
|
filePath = ENV['PWD'] || ::Dir.pwd
|
17
|
-
|
21
|
+
|
18
22
|
OptionParser.new do |opts|
|
19
23
|
opts.banner = "Usage: ZMediumFetcher [options]"
|
20
|
-
|
21
|
-
opts.on('-
|
22
|
-
|
23
|
-
fetcher.downloadPostsByUsername(username, outputFilePath)
|
24
|
-
|
25
|
-
Helper.printNewVersionMessageIfExists()
|
24
|
+
|
25
|
+
opts.on('-s', '--cookie_sid COOKIESID', 'Your logged-in Medium cookie sid value') do |cookie_sid|
|
26
|
+
$cookie_sid = cookie_sid
|
26
27
|
end
|
27
|
-
|
28
|
-
opts.on('-pPOST_URL', '--postURL=POST_URL', 'Downloading single post') do |postURL|
|
29
|
-
outputFilePath = PathPolicy.new("#{filePath}/Output", "Output")
|
30
|
-
fetcher.downloadPost(postURL, outputFilePath, nil)
|
31
28
|
|
32
|
-
|
29
|
+
opts.on('-d', '--cookie_uid COOKIEUID', 'Your logged-in Medium cookie uid value') do |cookie_uid|
|
30
|
+
$cookie_uid = cookie_uid
|
33
31
|
end
|
34
32
|
|
35
|
-
opts.on('-
|
36
|
-
|
37
|
-
fetcher.isForJekyll = true
|
38
|
-
fetcher.downloadPostsByUsername(username, outputFilePath)
|
39
|
-
|
40
|
-
Helper.printNewVersionMessageIfExists()
|
33
|
+
opts.on('-u', '--username USERNAME', 'Downloading all posts from user') do |username|
|
34
|
+
options[:u] = username
|
41
35
|
end
|
42
36
|
|
43
|
-
opts.on('-
|
44
|
-
|
45
|
-
|
46
|
-
fetcher.downloadPost(postURL, outputFilePath, nil)
|
37
|
+
opts.on('-p', '--postURL POST_URL', 'Downloading single post') do |postURL|
|
38
|
+
options[:p] = postURL
|
39
|
+
end
|
47
40
|
|
48
|
-
|
41
|
+
opts.on('-j', '--jekyllUsername USERNAME', 'Downloading all posts from user with Jekyll friendly') do |username|
|
42
|
+
options[:j] = username
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on('-k', '--jekyllPostURL POST_URL', 'Downloading single post with Jekyll friendly') do |postURL|
|
46
|
+
options[:k] = postURL
|
49
47
|
end
|
50
48
|
|
51
49
|
opts.on('-n', '--new', 'Update to latest version') do
|
52
|
-
|
53
|
-
Helper.downloadLatestVersion()
|
54
|
-
else
|
55
|
-
puts "You're using the latest version :)"
|
56
|
-
end
|
50
|
+
options[:n] = true
|
57
51
|
end
|
58
52
|
|
59
53
|
opts.on('-c', '--clean', 'Remove all downloaded posts data') do
|
60
|
-
|
61
|
-
FileUtils.rm_rf(Dir[outputFilePath.getAbsolutePath(nil)])
|
62
|
-
puts "All downloaded posts data has been removed."
|
63
|
-
|
64
|
-
Helper.printNewVersionMessageIfExists()
|
54
|
+
options[:c] = true
|
65
55
|
end
|
66
56
|
|
67
57
|
opts.on('-v', '--version', 'Print current ZMediumToMarkdown Version & Output Path') do
|
68
|
-
|
69
|
-
|
70
|
-
Helper.printNewVersionMessageIfExists()
|
58
|
+
options[:v] = true
|
71
59
|
end
|
72
|
-
|
73
60
|
end.parse!
|
61
|
+
|
62
|
+
#
|
63
|
+
if !options[:v].nil? && options[:v] == true
|
64
|
+
puts "Version:#{Helper.getLocalVersion().to_s}"
|
65
|
+
|
66
|
+
Helper.printNewVersionMessageIfExists()
|
67
|
+
elsif !options[:c].nil? && options[:c] == true
|
68
|
+
outputFilePath = PathPolicy.new(filePath, "")
|
69
|
+
FileUtils.rm_rf(Dir[outputFilePath.getAbsolutePath(nil)])
|
70
|
+
puts "All downloaded posts data has been removed."
|
71
|
+
|
72
|
+
Helper.printNewVersionMessageIfExists()
|
73
|
+
elsif !options[:n].nil? && options[:n] == true
|
74
|
+
if Helper.getRemoteVersionFromGithub() > Helper.getLocalVersion()
|
75
|
+
Helper.downloadLatestVersion()
|
76
|
+
else
|
77
|
+
puts "You're using the latest version :)"
|
78
|
+
end
|
79
|
+
elsif !options[:k].nil?
|
80
|
+
postURL = options[:k]
|
81
|
+
|
82
|
+
outputFilePath = PathPolicy.new(filePath, "")
|
83
|
+
fetcher.isForJekyll = true
|
84
|
+
fetcher.downloadPost(postURL, outputFilePath, nil)
|
85
|
+
|
86
|
+
Helper.printNewVersionMessageIfExists()
|
87
|
+
elsif !options[:j].nil?
|
88
|
+
username = options[:j]
|
89
|
+
|
90
|
+
outputFilePath = PathPolicy.new(filePath, "")
|
91
|
+
fetcher.isForJekyll = true
|
92
|
+
fetcher.downloadPostsByUsername(username, outputFilePath)
|
93
|
+
|
94
|
+
Helper.printNewVersionMessageIfExists()
|
95
|
+
elsif !options[:p].nil?
|
96
|
+
postURL = options[:p]
|
97
|
+
|
98
|
+
outputFilePath = PathPolicy.new("#{filePath}/Output", "Output")
|
99
|
+
fetcher.downloadPost(postURL, outputFilePath, nil)
|
100
|
+
|
101
|
+
Helper.printNewVersionMessageIfExists()
|
102
|
+
elsif !options[:u].nil?
|
103
|
+
username = options[:u]
|
104
|
+
|
105
|
+
outputFilePath = PathPolicy.new("#{filePath}/Output", "Output")
|
106
|
+
fetcher.downloadPostsByUsername(username, outputFilePath)
|
107
|
+
|
108
|
+
Helper.printNewVersionMessageIfExists()
|
109
|
+
end
|
110
|
+
|
74
111
|
end
|
75
112
|
end
|
76
113
|
|
data/lib/Helper.rb
CHANGED
@@ -99,7 +99,7 @@ class Helper
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
-
def self.createPostInfo(postInfo, isPin, isForJekyll)
|
102
|
+
def self.createPostInfo(postInfo, isPin, isLockedPreviewOnly, isForJekyll)
|
103
103
|
title = postInfo.title&.gsub("[","")
|
104
104
|
title = title&.gsub("]","")
|
105
105
|
|
@@ -123,6 +123,9 @@ class Helper
|
|
123
123
|
if !isPin.nil? && isPin == true
|
124
124
|
result += "pin: true\r\n"
|
125
125
|
end
|
126
|
+
if !isLockedPreviewOnly.nil? && isLockedPreviewOnly == true
|
127
|
+
result += "lockedPreviewOnly: true\r\n"
|
128
|
+
end
|
126
129
|
|
127
130
|
if isForJekyll
|
128
131
|
result += "render_with_liquid: false\n"
|
@@ -211,4 +214,17 @@ class Helper
|
|
211
214
|
|
212
215
|
text
|
213
216
|
end
|
217
|
+
|
218
|
+
def self.createViewFullPost(postURL, isForJekyll)
|
219
|
+
jekyllOpen = ""
|
220
|
+
if isForJekyll
|
221
|
+
jekyllOpen = "{:target=\"_blank\"}"
|
222
|
+
end
|
223
|
+
|
224
|
+
text = "\r\n\r\n\r\n"
|
225
|
+
text += "**This [post](#{postURL})#{jekyllOpen} is behind Medium's paywall, View the full [post](#{postURL})#{jekyllOpen} on Medium, converted by [ZMediumToMarkdown](https://github.com/ZhgChgLi/ZMediumToMarkdown)#{jekyllOpen}.**"
|
226
|
+
text += "\r\n"
|
227
|
+
|
228
|
+
text
|
229
|
+
end
|
214
230
|
end
|
data/lib/Post.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
$lib = File.expand_path('../lib', File.dirname(__FILE__))
|
2
2
|
|
3
3
|
require "Request"
|
4
|
+
require "net/http"
|
4
5
|
require 'uri'
|
5
6
|
require 'nokogiri'
|
6
7
|
require 'json'
|
@@ -50,10 +51,22 @@ class Post
|
|
50
51
|
}
|
51
52
|
]
|
52
53
|
|
53
|
-
|
54
|
+
uri = URI("https://medium.com/_/graphql")
|
55
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
56
|
+
https.use_ssl = true
|
57
|
+
request = Net::HTTP::Post.new(uri)
|
58
|
+
request['Content-Type'] = 'application/json'
|
59
|
+
if !$cookie_sid.nil? && !$cookie_uid.nil?
|
60
|
+
request['Cookie'] = "sid=#{$cookie_sid}; uid=#{$cookie_uid}"
|
61
|
+
end
|
62
|
+
request.body = JSON.dump(query)
|
63
|
+
response = https.request(request)
|
64
|
+
|
65
|
+
|
66
|
+
body = Request.body(response)
|
54
67
|
if !body.nil?
|
55
68
|
json = JSON.parse(body)
|
56
|
-
json&.dig(0, "data", "post", "viewerEdge", "fullContent"
|
69
|
+
json&.dig(0, "data", "post", "viewerEdge", "fullContent")
|
57
70
|
else
|
58
71
|
nil
|
59
72
|
end
|
data/lib/ZMediumFetcher.rb
CHANGED
@@ -148,8 +148,16 @@ class ZMediumFetcher
|
|
148
148
|
end
|
149
149
|
|
150
150
|
postInfo = Post.parsePostInfoFromPostContent(postContent, postID, imagePathPolicy)
|
151
|
+
contentInfo = Post.fetchPostParagraphs(postID)
|
152
|
+
|
153
|
+
if contentInfo.nil?
|
154
|
+
raise "Error: Paragraph Content not found! PostURL: #{postURL}"
|
155
|
+
end
|
156
|
+
|
157
|
+
isLockedPreviewOnly = contentInfo&.dig("isLockedPreviewOnly")
|
158
|
+
|
159
|
+
sourceParagraphs = contentInfo&.dig("bodyModel", "paragraphs")
|
151
160
|
|
152
|
-
sourceParagraphs = Post.fetchPostParagraphs(postID)
|
153
161
|
if sourceParagraphs.nil?
|
154
162
|
raise "Error: Paragraph not found! PostURL: #{postURL}"
|
155
163
|
end
|
@@ -236,6 +244,7 @@ class ZMediumFetcher
|
|
236
244
|
|
237
245
|
fileLatestPublishedAt = nil
|
238
246
|
filePin = false
|
247
|
+
fileLockedPreviewOnly = false
|
239
248
|
if File.file?(absolutePath)
|
240
249
|
lines = File.foreach(absolutePath).first(15)
|
241
250
|
if lines.first&.start_with?("---")
|
@@ -248,10 +257,15 @@ class ZMediumFetcher
|
|
248
257
|
if !pinLine.nil?
|
249
258
|
filePin = pinLine[/^(pin:)\s+(\S*)/, 2].downcase == "true"
|
250
259
|
end
|
260
|
+
|
261
|
+
lockedPreviewOnlyLine = lines.select { |line| line.start_with?("lockedPreviewOnly:") }.first
|
262
|
+
if !lockedPreviewOnlyLine.nil?
|
263
|
+
fileLockedPreviewOnly = lockedPreviewOnlyLine[/^(lockedPreviewOnly:)\s+(\S*)/, 2].downcase == "true"
|
264
|
+
end
|
251
265
|
end
|
252
266
|
end
|
253
267
|
|
254
|
-
if (!fileLatestPublishedAt.nil? && fileLatestPublishedAt >= postInfo.latestPublishedAt.to_i) && (!isPin.nil? && isPin == filePin)
|
268
|
+
if (!fileLatestPublishedAt.nil? && fileLatestPublishedAt >= postInfo.latestPublishedAt.to_i) && (!isPin.nil? && isPin == filePin) && (!isLockedPreviewOnly.nil? && isLockedPreviewOnly == fileLockedPreviewOnly)
|
255
269
|
# Already downloaded and nothing has changed!, Skip!
|
256
270
|
progress.currentPostParagraphIndex = paragraphs.length
|
257
271
|
progress.message = "Skip, Post already downloaded and nothing has changed!"
|
@@ -260,7 +274,7 @@ class ZMediumFetcher
|
|
260
274
|
Helper.createDirIfNotExist(postPathPolicy.getAbsolutePath(nil))
|
261
275
|
File.open(absolutePath, "w+") do |file|
|
262
276
|
# write postInfo into top
|
263
|
-
postMetaInfo = Helper.createPostInfo(postInfo, isPin, isForJekyll)
|
277
|
+
postMetaInfo = Helper.createPostInfo(postInfo, isPin, isLockedPreviewOnly, isForJekyll)
|
264
278
|
if !postMetaInfo.nil?
|
265
279
|
file.puts(postMetaInfo)
|
266
280
|
end
|
@@ -283,15 +297,29 @@ class ZMediumFetcher
|
|
283
297
|
progress.message = "Converting Post..."
|
284
298
|
progress.printLog()
|
285
299
|
end
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
300
|
+
|
301
|
+
if isLockedPreviewOnly
|
302
|
+
viewFullPost = Helper.createViewFullPost(postURL, isForJekyll)
|
303
|
+
if !viewFullPost.nil?
|
304
|
+
file.puts(viewFullPost)
|
305
|
+
end
|
306
|
+
else
|
307
|
+
postWatermark = Helper.createWatermark(postURL, isForJekyll)
|
308
|
+
if !postWatermark.nil?
|
309
|
+
file.puts(postWatermark)
|
310
|
+
end
|
290
311
|
end
|
312
|
+
|
313
|
+
|
291
314
|
end
|
292
315
|
FileUtils.touch absolutePath, :mtime => postInfo.latestPublishedAt
|
293
316
|
|
294
|
-
|
317
|
+
if isLockedPreviewOnly
|
318
|
+
progress.message = "This post is behind Medium's paywall. You need to provide valid Medium Member login cookies to download the full post."
|
319
|
+
else
|
320
|
+
progress.message = "Post Successfully Downloaded!"
|
321
|
+
end
|
322
|
+
|
295
323
|
progress.printLog()
|
296
324
|
end
|
297
325
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ZMediumToMarkdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ZhgChgLi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|