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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6aa04febbe53fb63849ed9bc0fdd83c1e30b67e808f42899e9ec2d4772db57bd
4
- data.tar.gz: '09fb6a627ecb9c320b114113713464871d60fe1e2c32810ecba4c77ea11e5381'
3
+ metadata.gz: d8a5ca887a7781219ae534d50ebe022c796d21ddc6eb5fab977559fd65ce4cb2
4
+ data.tar.gz: 4e43067b4502adc9a11cf3347c68091980c273e567d22811e6d4d02b233ef5a8
5
5
  SHA512:
6
- metadata.gz: 3e58ac4d9c2f00113e9446594f75067c2f9a48a3c8c35f2ef752da024c19e18c77eb3f03f816b53a7c0c8fd6603b2790d08cd5986e6de7d858c555f0d17c9e91
7
- data.tar.gz: eab3c050b0fdc2cfc0c85518cabdc4d9d3a1eda6de220046639fb76ccdb133540e9408640a1dec9b48574933f465d130b988fe072af6b40e79a78cd719b065db
6
+ metadata.gz: 8e7ecbc20c8038097d213bfce93f6129b8451f7a990ad97c04f07533b6b9c0be8c0a913d4599d19b8aa6ca8e8d95724da4190f6f55c5678c5e944078d022a390
7
+ data.tar.gz: 7ea56ec01d108d51a1f56087e28a5596a7e865dbc937507ccf2e029ed8ac1f536225a940437d30a5e36ab3a74e7c1fef38b410f0adc7b6081144baafdcbd3e4b
@@ -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('-uUSERNAME', '--username=USERNAME', 'Downloading all posts from user') do |username|
22
- outputFilePath = PathPolicy.new("#{filePath}/Output", "Output")
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
- Helper.printNewVersionMessageIfExists()
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('-jUSERNAME', '--jekyllUsername=USERNAME', 'Downloading all posts from user with Jekyll friendly') do |username|
36
- outputFilePath = PathPolicy.new(filePath, "")
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('-kPOST_URL', '--jekyllPostURL=POST_URL', 'Downloading single post with Jekyll friendly') do |postURL|
44
- outputFilePath = PathPolicy.new(filePath, "")
45
- fetcher.isForJekyll = true
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
- Helper.printNewVersionMessageIfExists()
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
- if Helper.getRemoteVersionFromGithub() > Helper.getLocalVersion()
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
- outputFilePath = PathPolicy.new(filePath, "")
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
- puts "Version:#{Helper.getLocalVersion().to_s}"
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
- body = Request.body(Request.URL("https://medium.com/_/graphql", "POST", query))
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", "bodyModel", "paragraphs")
69
+ json&.dig(0, "data", "post", "viewerEdge", "fullContent")
57
70
  else
58
71
  nil
59
72
  end
@@ -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
- postWatermark = Helper.createWatermark(postURL, isForJekyll)
288
- if !postWatermark.nil?
289
- file.puts(postWatermark)
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
- progress.message = "Post Successfully Downloaded!"
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.3.8
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-05-26 00:00:00.000000000 Z
11
+ date: 2024-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri