ZMediumToMarkdown 2.3.8 → 2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|