Gmeek/Gmeek.py
awesomeoxc 1a4f90eae9 增加 disableCamo 配置项,控制文章中的图片是否使用创建文章(issue)时输入的原始链接。
背景:
1. Gmeek 使用了 GitHub API 将 markdown 渲染为 html;
2. 而在这个过程 GitHub 会使用开源项目 Camo 将 markdown 中的图片进行托管;
3. Camo 会为每幅图像生成开头为 https://camo.githubusercontent.com/ 的匿名 URL 代理;

Github 这么做的理由是:
1. 代理这些图像将有助于保护用户的隐私:用户访问这些图片时,浏览器信息不会泄露给第三方服务;
2. Github 通过 CDN 加速图像的访问;
3. 有 CDN 可以减少因原始图片链接失效而无法正常渲染的情况;

然而如果你的图片本身托管在国内,GitHub 这么做反而会降低图片的访问速度,甚至导致无法访问(不能科学上网时)。
因此,增加一个单独的配置项,如果配置了 `"disableCamo": 1`,Gmeek 会在 GitHub 渲染网页后将图片链接简单替换为原始链接。
2024-08-01 16:20:13 +08:00

543 lines
29 KiB
Python

# -*- coding: utf-8 -*-
import os
import re
import json
import time
import datetime
import shutil
import urllib
import requests
import argparse
import html
from github import Github
from xpinyin import Pinyin
from feedgen.feed import FeedGenerator
from jinja2 import Environment, FileSystemLoader
from transliterate import translit
from collections import OrderedDict
######################################################################################
i18n={"Search":"Search","switchTheme":"switch theme","home":"home","comments":"comments","run":"run ","days":" days","Previous":"Previous","Next":"Next"}
i18nCN={"Search":"搜索","switchTheme":"切换主题","home":"首页","comments":"评论","run":"网站运行","days":"","Previous":"上一页","Next":"下一页"}
i18nRU={"Search":"Поиск","switchTheme": "Сменить тему","home":"Главная","comments":"Комментарии","run":"работает ","days":" дней","Previous":"Предыдущая","Next":"Следующая"}
IconBase={
"post":"M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25ZM3.5 6.25a.75.75 0 0 1 .75-.75h7a.75.75 0 0 1 0 1.5h-7a.75.75 0 0 1-.75-.75Zm.75 2.25h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1 0-1.5Z",
"link":"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z",
"about":"M10.561 8.073a6.005 6.005 0 0 1 3.432 5.142.75.75 0 1 1-1.498.07 4.5 4.5 0 0 0-8.99 0 .75.75 0 0 1-1.498-.07 6.004 6.004 0 0 1 3.431-5.142 3.999 3.999 0 1 1 5.123 0ZM10.5 5a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z",
"sun":"M8 10.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM8 12a4 4 0 100-8 4 4 0 000 8zM8 0a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0V.75A.75.75 0 018 0zm0 13a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 018 13zM2.343 2.343a.75.75 0 011.061 0l1.06 1.061a.75.75 0 01-1.06 1.06l-1.06-1.06a.75.75 0 010-1.06zm9.193 9.193a.75.75 0 011.06 0l1.061 1.06a.75.75 0 01-1.06 1.061l-1.061-1.06a.75.75 0 010-1.061zM16 8a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0116 8zM3 8a.75.75 0 01-.75.75H.75a.75.75 0 010-1.5h1.5A.75.75 0 013 8zm10.657-5.657a.75.75 0 010 1.061l-1.061 1.06a.75.75 0 11-1.06-1.06l1.06-1.06a.75.75 0 011.06 0zm-9.193 9.193a.75.75 0 010 1.06l-1.06 1.061a.75.75 0 11-1.061-1.06l1.06-1.061a.75.75 0 011.061 0z",
"moon":"M9.598 1.591a.75.75 0 01.785-.175 7 7 0 11-8.967 8.967.75.75 0 01.961-.96 5.5 5.5 0 007.046-7.046.75.75 0 01.175-.786zm1.616 1.945a7 7 0 01-7.678 7.678 5.5 5.5 0 107.678-7.678z",
"search":"M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z",
"rss":"M2.002 2.725a.75.75 0 0 1 .797-.699C8.79 2.42 13.58 7.21 13.974 13.201a.75.75 0 0 1-1.497.098 10.502 10.502 0 0 0-9.776-9.776.747.747 0 0 1-.7-.798ZM2.84 7.05h-.002a7.002 7.002 0 0 1 6.113 6.111.75.75 0 0 1-1.49.178 5.503 5.503 0 0 0-4.8-4.8.75.75 0 0 1 .179-1.489ZM2 13a1 1 0 1 1 2 0 1 1 0 0 1-2 0Z",
"upload":"M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z M11.78 4.72a.749.749 0 1 1-1.06 1.06L8.75 3.811V9.5a.75.75 0 0 1-1.5 0V3.811L5.28 5.78a.749.749 0 1 1-1.06-1.06l3.25-3.25a.749.749 0 0 1 1.06 0l3.25 3.25Z",
"github":"M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z",
"home":"M6.906.664a1.749 1.749 0 0 1 2.187 0l5.25 4.2c.415.332.657.835.657 1.367v7.019A1.75 1.75 0 0 1 13.25 15h-3.5a.75.75 0 0 1-.75-.75V9H7v5.25a.75.75 0 0 1-.75.75h-3.5A1.75 1.75 0 0 1 1 13.25V6.23c0-.531.242-1.034.657-1.366l5.25-4.2Zm1.25 1.171a.25.25 0 0 0-.312 0l-5.25 4.2a.25.25 0 0 0-.094.196v7.019c0 .138.112.25.25.25H5.5V8.25a.75.75 0 0 1 .75-.75h3.5a.75.75 0 0 1 .75.75v5.25h2.75a.25.25 0 0 0 .25-.25V6.23a.25.25 0 0 0-.094-.195Z",
"sync":"M1.705 8.005a.75.75 0 0 1 .834.656 5.5 5.5 0 0 0 9.592 2.97l-1.204-1.204a.25.25 0 0 1 .177-.427h3.646a.25.25 0 0 1 .25.25v3.646a.25.25 0 0 1-.427.177l-1.38-1.38A7.002 7.002 0 0 1 1.05 8.84a.75.75 0 0 1 .656-.834ZM8 2.5a5.487 5.487 0 0 0-4.131 1.869l1.204 1.204A.25.25 0 0 1 4.896 6H1.25A.25.25 0 0 1 1 5.75V2.104a.25.25 0 0 1 .427-.177l1.38 1.38A7.002 7.002 0 0 1 14.95 7.16a.75.75 0 0 1-1.49.178A5.5 5.5 0 0 0 8 2.5Z"
}
######################################################################################
class GMEEK():
def __init__(self,options):
self.options=options
self.root_dir='docs/'
self.static_dir='static/'
self.post_folder='post/'
self.backup_dir='backup/'
self.post_dir=self.root_dir+self.post_folder
user = Github(self.options.github_token)
self.repo = self.get_repo(user, options.repo_name)
self.feed = FeedGenerator()
self.oldFeedString=''
self.labelColorDict=json.loads('{}')
for label in self.repo.get_labels():
self.labelColorDict[label.name]='#'+label.color
print(self.labelColorDict)
self.defaultConfig()
def cleanFile(self):
workspace_path = os.environ.get('GITHUB_WORKSPACE')
if os.path.exists(workspace_path+"/"+self.backup_dir):
shutil.rmtree(workspace_path+"/"+self.backup_dir)
if os.path.exists(workspace_path+"/"+self.root_dir):
shutil.rmtree(workspace_path+"/"+self.root_dir)
if os.path.exists(self.backup_dir):
shutil.rmtree(self.backup_dir)
if os.path.exists(self.root_dir):
shutil.rmtree(self.root_dir)
os.mkdir(self.backup_dir)
os.mkdir(self.root_dir)
os.mkdir(self.post_dir)
if os.path.exists(self.static_dir):
for item in os.listdir(self.static_dir):
src = os.path.join(self.static_dir, item)
dst = os.path.join(self.root_dir, item)
if os.path.isfile(src):
shutil.copy(src, dst)
print(f"Copied {item} to docs")
elif os.path.isdir(src):
shutil.copytree(src, dst)
print(f"Copied directory {item} to docs")
else:
print("static does not exist")
def defaultConfig(self):
dconfig={"singlePage":[],"startSite":"","filingNum":"","onePageListNum":15,"commentLabelColor":"#006b75","yearColorList":["#bc4c00", "#0969da", "#1f883d", "#A333D0"],"i18n":"CN","themeMode":"manual","dayTheme":"light","nightTheme":"dark","urlMode":"pinyin","script":"","style":"","head":"","indexScript":"","indexStyle":"","bottomText":"","showPostSource":1,"iconList":{},"UTC":+8,"rssSplit":"sentence","exlink":{},"needComment":1,"allHead":"","disableCamo":0}
config=json.loads(open('config.json', 'r', encoding='utf-8').read())
self.blogBase={**dconfig,**config}.copy()
self.blogBase["postListJson"]=json.loads('{}')
self.blogBase["singeListJson"]=json.loads('{}')
self.blogBase["labelColorDict"]=self.labelColorDict
if "displayTitle" not in self.blogBase:
self.blogBase["displayTitle"]=self.blogBase["title"]
if "faviconUrl" not in self.blogBase:
self.blogBase["faviconUrl"]=self.blogBase["avatarUrl"]
if "ogImage" not in self.blogBase:
self.blogBase["ogImage"]=self.blogBase["avatarUrl"]
if "primerCSS" not in self.blogBase:
self.blogBase["primerCSS"]="<link href='https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/Primer/21.0.7/primer.css' rel='stylesheet' />"
if "homeUrl" not in self.blogBase:
if str(self.repo.name).lower() == (str(self.repo.owner.login) + ".github.io").lower():
self.blogBase["homeUrl"] = f"https://{self.repo.name}"
else:
self.blogBase["homeUrl"] = f"https://{self.repo.owner.login}.github.io/{self.repo.name}"
print("GitHub Pages URL: ", self.blogBase["homeUrl"])
if self.blogBase["i18n"]=="CN":
self.i18n=i18nCN
elif self.blogBase["i18n"]=="RU":
self.i18n=i18nRU
else:
self.i18n=i18n
self.TZ=datetime.timezone(datetime.timedelta(hours=self.blogBase["UTC"]))
def get_repo(self,user:Github, repo:str):
return user.get_repo(repo)
def markdown2html(self, mdstr):
payload = {"text": mdstr, "mode": "gfm"}
headers = {"Authorization": "token {}".format(self.options.github_token)}
try:
response = requests.post("https://api.github.com/markdown", json=payload, headers=headers)
response.raise_for_status() # Raises an exception if status code is not 200
return response.text
except requests.RequestException as e:
raise Exception("markdown2html error: {}".format(e))
def renderHtml(self,template,blogBase,postListJson,htmlDir,icon):
file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)
template = env.get_template(template)
output = template.render(blogBase=blogBase,postListJson=postListJson,i18n=self.i18n,IconList=icon)
f = open(htmlDir, 'w', encoding='UTF-8')
f.write(output)
f.close()
@staticmethod
def replace_canonical_attributes(match):
a_tag = match.group(1)
img_tag = match.group(2)
# 提取data-canonical-src的值
data_canonical_src = re.search(r'data-canonical-src="([^"]*)"', img_tag)
if data_canonical_src:
canonical_src = data_canonical_src.group(1)
# 替换a标签的href属性
a_tag = re.sub(r'href="[^"]*"', f'href="{canonical_src}"', a_tag)
# 替换img标签的src属性
img_tag = re.sub(r'src="[^"]*"', f'src="{canonical_src}"', img_tag)
# 删除data-canonical-src属性
img_tag = re.sub(r'\s*data-canonical-src="[^"]*"', '', img_tag)
return a_tag + img_tag
def process_html_img_src(self, origin_html):
if self.blogBase["disableCamo"] != 1:
return origin_html
# 使用正则表达式查找并替换符合条件的标签
pattern = r'(<a[^>]*>)(<img[^>]*data-canonical-src[^>]*>)'
processed_html = re.sub(pattern, self.replace_canonical_attributes, origin_html)
return processed_html
def createPostHtml(self,issue):
mdFileName=re.sub(r'[<>:/\\|?*\"]|[\0-\31]', '-', issue["postTitle"])
f = open(self.backup_dir+mdFileName+".md", 'r', encoding='UTF-8')
origin_post_body = self.markdown2html(f.read())
f.close()
post_body = self.process_html_img_src(origin_post_body)
postBase=self.blogBase.copy()
if '<math-renderer' in post_body:
post_body=re.sub(r'<math-renderer.*?>','',post_body)
post_body=re.sub(r'</math-renderer>','',post_body)
issue["script"]=issue["script"]+'<script>MathJax = {tex: {inlineMath: [["$", "$"]]}};</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>'
if '<p class="markdown-alert-title">' in post_body:
issue["style"]=issue["style"]+'<style>.markdown-alert{padding:0.5rem 1rem;margin-bottom:1rem;border-left:.25em solid var(--borderColor-default,var(--color-border-default));}.markdown-alert .markdown-alert-title {display:flex;font-weight:var(--base-text-weight-medium,500);align-items:center;line-height:1;}.markdown-alert>:first-child {margin-top:0;}.markdown-alert>:last-child {margin-bottom:0;}</style>'
alerts = {
'note': 'accent',
'tip': 'success',
'important': 'done',
'warning': 'attention',
'caution': 'danger'
}
for alert, style in alerts.items():
if f'markdown-alert-{alert}' in post_body:
issue["style"] += (
f'<style>.markdown-alert.markdown-alert-{alert} {{'
f'border-left-color:var(--borderColor-{style}-emphasis, var(--color-{style}-emphasis));'
f'background-color:var(--color-{style}-subtle);}}'
f'.markdown-alert.markdown-alert-{alert} .markdown-alert-title {{'
f'color: var(--fgColor-{style},var(--color-{style}-fg));}}</style>'
)
if '<code class="notranslate">Gmeek-html' in post_body:
post_body = re.sub(r'<code class="notranslate">Gmeek-html(.*?)</code>', lambda match: html.unescape(match.group(1)), post_body, flags=re.DOTALL)
postBase["postTitle"]=issue["postTitle"]
postBase["postUrl"]=self.blogBase["homeUrl"]+"/"+issue["postUrl"]
postBase["description"]=issue["description"]
postBase["ogImage"]=issue["ogImage"]
postBase["postBody"]=post_body
postBase["commentNum"]=issue["commentNum"]
postBase["style"]=issue["style"]
postBase["script"]=issue["script"]
postBase["head"]=issue["head"]
postBase["top"]=issue["top"]
postBase["postSourceUrl"]=issue["postSourceUrl"]
postBase["repoName"]=options.repo_name
if "highlight" in post_body:
postBase["highlight"]=1
else:
postBase["highlight"]=0
if issue["labels"][0] in self.blogBase["singlePage"]:
postBase["bottomText"]=''
keys=['sun','moon','sync','home','github']
postIcon=dict(zip(keys, map(IconBase.get, keys)))
self.renderHtml('post.html',postBase,{},issue["htmlDir"],postIcon)
print("create postPage title=%s file=%s " % (issue["postTitle"],issue["htmlDir"]))
def createPlistHtml(self):
self.blogBase["postListJson"]=dict(sorted(self.blogBase["postListJson"].items(),key=lambda x:(x[1]["top"],x[1]["createdAt"]),reverse=True))#使列表由时间排序
keys=list(OrderedDict.fromkeys(['sun', 'moon','sync', 'search', 'rss', 'upload', 'post'] + self.blogBase["singlePage"]))
plistIcon={**dict(zip(keys, map(IconBase.get, keys))),**self.blogBase["iconList"]}
keys=['sun','moon','sync','home','search','post']
tagIcon=dict(zip(keys, map(IconBase.get, keys)))
postNum=len(self.blogBase["postListJson"])
pageFlag=0
while True:
topNum=pageFlag*self.blogBase["onePageListNum"]
print("topNum=%d postNum=%d"%(topNum,postNum))
if postNum<=self.blogBase["onePageListNum"]:
if pageFlag==0:
onePageList=dict(list(self.blogBase["postListJson"].items())[:postNum])
htmlDir=self.root_dir+"index.html"
self.blogBase["prevUrl"]="disabled"
self.blogBase["nextUrl"]="disabled"
else:
onePageList=dict(list(self.blogBase["postListJson"].items())[topNum:topNum+postNum])
htmlDir=self.root_dir+("page%d.html" % (pageFlag+1))
if pageFlag==1:
self.blogBase["prevUrl"]="/index.html"
else:
self.blogBase["prevUrl"]="/page%d.html" % pageFlag
self.blogBase["nextUrl"]="disabled"
self.renderHtml('plist.html',self.blogBase,onePageList,htmlDir,plistIcon)
print("create "+htmlDir)
break
else:
onePageList=dict(list(self.blogBase["postListJson"].items())[topNum:topNum+self.blogBase["onePageListNum"]])
postNum=postNum-self.blogBase["onePageListNum"]
if pageFlag==0:
htmlDir=self.root_dir+"index.html"
self.blogBase["prevUrl"]="disabled"
self.blogBase["nextUrl"]="/page2.html"
else:
htmlDir=self.root_dir+("page%d.html" % (pageFlag+1))
if pageFlag==1:
self.blogBase["prevUrl"]="/index.html"
else:
self.blogBase["prevUrl"]="/page%d.html" % pageFlag
self.blogBase["nextUrl"]="/page%d.html" % (pageFlag+2)
self.renderHtml('plist.html',self.blogBase,onePageList,htmlDir,plistIcon)
print("create "+htmlDir)
pageFlag=pageFlag+1
self.renderHtml('tag.html',self.blogBase,onePageList,self.root_dir+"tag.html",tagIcon)
print("create tag.html")
def createFeedXml(self):
self.blogBase["postListJson"]=dict(sorted(self.blogBase["postListJson"].items(),key=lambda x:x[1]["createdAt"],reverse=False))#使列表由时间排序
feed = FeedGenerator()
feed.title(self.blogBase["title"])
feed.description(self.blogBase["subTitle"])
feed.link(href=self.blogBase["homeUrl"])
feed.image(url=self.blogBase["avatarUrl"],title="avatar", link=self.blogBase["homeUrl"])
feed.copyright(self.blogBase["title"])
feed.managingEditor(self.blogBase["title"])
feed.webMaster(self.blogBase["title"])
feed.ttl("60")
for num in self.blogBase["singeListJson"]:
item=feed.add_item()
item.guid(self.blogBase["homeUrl"]+"/"+self.blogBase["singeListJson"][num]["postUrl"],permalink=True)
item.title(self.blogBase["singeListJson"][num]["postTitle"])
item.description(self.blogBase["singeListJson"][num]["description"])
item.link(href=self.blogBase["homeUrl"]+"/"+self.blogBase["singeListJson"][num]["postUrl"])
item.pubDate(time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(self.blogBase["singeListJson"][num]["createdAt"])))
for num in self.blogBase["postListJson"]:
item=feed.add_item()
item.guid(self.blogBase["homeUrl"]+"/"+self.blogBase["postListJson"][num]["postUrl"],permalink=True)
item.title(self.blogBase["postListJson"][num]["postTitle"])
item.description(self.blogBase["postListJson"][num]["description"])
item.link(href=self.blogBase["homeUrl"]+"/"+self.blogBase["postListJson"][num]["postUrl"])
item.pubDate(time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(self.blogBase["postListJson"][num]["createdAt"])))
if self.oldFeedString!='':
feed.rss_file(self.root_dir+'new.xml')
newFeed=open(self.root_dir+'new.xml','r',encoding='utf-8')
new=newFeed.read()
newFeed.close()
new=re.sub(r'<lastBuildDate>.*?</lastBuildDate>','',new)
old=re.sub(r'<lastBuildDate>.*?</lastBuildDate>','',self.oldFeedString)
os.remove(self.root_dir+'new.xml')
if new==old:
print("====== rss xml no update ======")
feedFile=open(self.root_dir+'rss.xml',"w")
feedFile.write(self.oldFeedString)
feedFile.close()
return
print("====== create rss xml ======")
feed.rss_file(self.root_dir+'rss.xml')
def addOnePostJson(self,issue):
if len(issue.labels)>=1:
if issue.labels[0].name in self.blogBase["singlePage"]:
listJsonName='singeListJson'
htmlFile='{}.html'.format(self.createFileName(issue,useLabel=True))
gen_Html = self.root_dir+htmlFile
else:
listJsonName='postListJson'
htmlFile='{}.html'.format(self.createFileName(issue))
gen_Html = self.post_dir+htmlFile
postNum="P"+str(issue.number)
self.blogBase[listJsonName][postNum]=json.loads('{}')
self.blogBase[listJsonName][postNum]["htmlDir"]=gen_Html
self.blogBase[listJsonName][postNum]["labels"]=[label.name for label in issue.labels]
# self.blogBase[listJsonName][postNum]["labelColor"]=self.labelColorDict[issue.labels[0].name]
self.blogBase[listJsonName][postNum]["postTitle"]=issue.title
self.blogBase[listJsonName][postNum]["postUrl"]=urllib.parse.quote(gen_Html[len(self.root_dir):])
self.blogBase[listJsonName][postNum]["postSourceUrl"]="https://github.com/"+options.repo_name+"/issues/"+str(issue.number)
self.blogBase[listJsonName][postNum]["commentNum"]=issue.get_comments().totalCount
if issue.body==None:
self.blogBase[listJsonName][postNum]["description"]=''
self.blogBase[listJsonName][postNum]["wordCount"]=0
else:
self.blogBase[listJsonName][postNum]["wordCount"]=len(issue.body)
if self.blogBase["rssSplit"]=="sentence":
if self.blogBase["i18n"]=="CN":
period=""
else:
period="."
else:
period=self.blogBase["rssSplit"]
self.blogBase[listJsonName][postNum]["description"]=issue.body.split(period)[0].replace("\"", "\'")+period
self.blogBase[listJsonName][postNum]["top"]=0
for event in issue.get_events():
if event.event=="pinned":
self.blogBase[listJsonName][postNum]["top"]=1
elif event.event=="unpinned":
self.blogBase[listJsonName][postNum]["top"]=0
try:
postConfig=json.loads(issue.body.split("\r\n")[-1:][0].split("##")[1])
print("Has Custom JSON parameters")
print(postConfig)
except:
postConfig={}
if "timestamp" in postConfig:
self.blogBase[listJsonName][postNum]["createdAt"]=postConfig["timestamp"]
else:
self.blogBase[listJsonName][postNum]["createdAt"]=int(time.mktime(issue.created_at.timetuple()))
if "style" in postConfig:
self.blogBase[listJsonName][postNum]["style"]=self.blogBase["style"]+str(postConfig["style"])
else:
self.blogBase[listJsonName][postNum]["style"]=self.blogBase["style"]
if "script" in postConfig:
self.blogBase[listJsonName][postNum]["script"]=self.blogBase["script"]+str(postConfig["script"])
else:
self.blogBase[listJsonName][postNum]["script"]=self.blogBase["script"]
if "head" in postConfig:
self.blogBase[listJsonName][postNum]["head"]=self.blogBase["head"]+str(postConfig["head"])
else:
self.blogBase[listJsonName][postNum]["head"]=self.blogBase["head"]
if "ogImage" in postConfig:
self.blogBase[listJsonName][postNum]["ogImage"]=postConfig["ogImage"]
else:
self.blogBase[listJsonName][postNum]["ogImage"]=self.blogBase["ogImage"]
thisTime=datetime.datetime.fromtimestamp(self.blogBase[listJsonName][postNum]["createdAt"])
thisTime=thisTime.astimezone(self.TZ)
thisYear=thisTime.year
self.blogBase[listJsonName][postNum]["createdDate"]=thisTime.strftime("%Y-%m-%d")
self.blogBase[listJsonName][postNum]["dateLabelColor"]=self.blogBase["yearColorList"][int(thisYear)%len(self.blogBase["yearColorList"])]
mdFileName=re.sub(r'[<>:/\\|?*\"]|[\0-\31]', '-', issue.title)
f = open(self.backup_dir+mdFileName+".md", 'w', encoding='UTF-8')
if issue.body==None:
f.write('')
else:
f.write(issue.body)
f.close()
return listJsonName
def runAll(self):
print("====== start create static html ======")
self.cleanFile()
issues=self.repo.get_issues()
for issue in issues:
self.addOnePostJson(issue)
for issue in self.blogBase["postListJson"].values():
self.createPostHtml(issue)
for issue in self.blogBase["singeListJson"].values():
self.createPostHtml(issue)
self.createPlistHtml()
self.createFeedXml()
print("====== create static html end ======")
def runOne(self,number_str):
print("====== start create static html ======")
issue=self.repo.get_issue(int(number_str))
listJsonName=self.addOnePostJson(issue)
self.createPostHtml(self.blogBase[listJsonName]["P"+number_str])
self.createPlistHtml()
self.createFeedXml()
print("====== create static html end ======")
def createFileName(self,issue,useLabel=False):
if useLabel==True:
fileName=issue.labels[0].name
else:
if self.blogBase["urlMode"]=="issue":
fileName=str(issue.number)
elif self.blogBase["urlMode"]=="ru_translit":
fileName=str(translit(issue.title, language_code='ru', reversed=True)).replace(' ', '-')
else:
fileName=Pinyin().get_pinyin(issue.title)
fileName=re.sub(r'[<>:/\\|?*\"]|[\0-\31]', '-', fileName)
return fileName
######################################################################################
parser = argparse.ArgumentParser()
parser.add_argument("github_token", help="github_token")
parser.add_argument("repo_name", help="repo_name")
parser.add_argument("--issue_number", help="issue_number", default=0, required=False)
options = parser.parse_args()
blog=GMEEK(options)
if not os.path.exists("blogBase.json"):
print("blogBase is not exists, runAll")
blog.runAll()
else:
if os.path.exists(blog.root_dir+'rss.xml'):
oldFeedFile=open(blog.root_dir+'rss.xml','r',encoding='utf-8')
blog.oldFeedString=oldFeedFile.read()
oldFeedFile.close()
if options.issue_number=="0" or options.issue_number=="":
print("issue_number=='0', runAll")
blog.runAll()
else:
f=open("blogBase.json","r")
print("blogBase is exists and issue_number!=0, runOne")
oldBlogBase=json.loads(f.read())
for key, value in oldBlogBase.items():
blog.blogBase[key] = value
f.close()
blog.blogBase["labelColorDict"]=blog.labelColorDict
blog.runOne(options.issue_number)
listFile=open("blogBase.json","w")
listFile.write(json.dumps(blog.blogBase))
listFile.close()
commentNumSum=0
wordCount=0
print("====== create postList.json file ======")
blog.blogBase["postListJson"]=dict(sorted(blog.blogBase["postListJson"].items(),key=lambda x:x[1]["createdAt"],reverse=True))#使列表由时间排序
for i in blog.blogBase["postListJson"]:
del blog.blogBase["postListJson"][i]["description"]
del blog.blogBase["postListJson"][i]["postSourceUrl"]
del blog.blogBase["postListJson"][i]["htmlDir"]
del blog.blogBase["postListJson"][i]["createdAt"]
del blog.blogBase["postListJson"][i]["script"]
del blog.blogBase["postListJson"][i]["style"]
del blog.blogBase["postListJson"][i]["top"]
del blog.blogBase["postListJson"][i]["ogImage"]
if 'head' in blog.blogBase["postListJson"][i]:
del blog.blogBase["postListJson"][i]["head"]
if 'commentNum' in blog.blogBase["postListJson"][i]:
commentNumSum=commentNumSum+blog.blogBase["postListJson"][i]["commentNum"]
del blog.blogBase["postListJson"][i]["commentNum"]
if 'wordCount' in blog.blogBase["postListJson"][i]:
wordCount=wordCount+blog.blogBase["postListJson"][i]["wordCount"]
del blog.blogBase["postListJson"][i]["wordCount"]
blog.blogBase["postListJson"]["labelColorDict"]=blog.labelColorDict
docListFile=open(blog.root_dir+"postList.json","w")
docListFile.write(json.dumps(blog.blogBase["postListJson"]))
docListFile.close()
if os.environ.get('GITHUB_EVENT_NAME')!='schedule':
print("====== update readme file ======")
workspace_path = os.environ.get('GITHUB_WORKSPACE')
readme="# %s :link: %s \r\n" % (blog.blogBase["title"],blog.blogBase["homeUrl"])
readme=readme+"### :page_facing_up: [%d](%s/tag.html) \r\n" % (len(blog.blogBase["postListJson"])-1,blog.blogBase["homeUrl"])
readme=readme+"### :speech_balloon: %d \r\n" % commentNumSum
readme=readme+"### :hibiscus: %d \r\n" % wordCount
readme=readme+"### :alarm_clock: %s \r\n" % datetime.datetime.now(blog.TZ).strftime('%Y-%m-%d %H:%M:%S')
readme=readme+"### Powered by :heart: [Gmeek](https://github.com/Meekdai/Gmeek)\r\n"
readmeFile=open(workspace_path+"/README.md","w")
readmeFile.write(readme)
readmeFile.close()
######################################################################################