cnblog_upload/cnblog_upload.command
| #! /bin/bash |
| |
| echo -n '请拖入一个md文件或者文件夹:' |
| read file_path |
| /Users/song/Code/script_python/cnblog_upload/venv/bin/python3 /Users/song/Code/script_python/cnblog_upload/src/main.py $file_path |
| |
| |
cnblog_upload/src/history2anki.py
| import os |
| |
| from server.cnblog_server import server |
| from conf.conf import ST |
| from file.csv import write2csv |
| |
| |
| if __name__ == "__main__": |
| recent_posts = server.metaWeblog.getRecentPosts(ST.blog_id,ST.username ,ST.password, 19) |
| |
| |
| out_md_path = os.path.join("/Users/song/Desktop" ,'anki.csv') |
| |
| for post in recent_posts: |
| link = post['link'] |
| content = post['description'].split('\n')[0] |
| write2csv(out_md_path,f'{content}\t<a href={link}>{content}</a>\n') |
cnblog_upload/src/main.py
| import asyncio |
| import time |
| import os |
| import sys |
| |
| from img.unload_img import find_md_img,replace_md_img,upload_md_img |
| from server.cnblog_server import server |
| from conf.conf import ST |
| from file.csv import write2csv,generate_file_name |
| from file.md import read_md,get_md_files |
| |
| net_images = [] |
| image_count = 1 |
| |
| |
| |
| def get_image_url(t): |
| """回调,获取url""" |
| global image_count |
| global net_images |
| url = t.result()['url'] |
| print(f'第{image_count}张图片上传成功,url:{url}') |
| net_images.append(url) |
| image_count += 1 |
| |
| |
| |
| |
| def upload_img(md_path,md): |
| global net_images |
| local_images = find_md_img(md) |
| |
| if local_images: |
| tasks = [] |
| for li in local_images: |
| image_full_path = os.path.join(dir_name, li) |
| task = asyncio.ensure_future(upload_md_img(image_full_path)) |
| task.add_done_callback(get_image_url) |
| tasks.append(task) |
| loop = asyncio.get_event_loop() |
| loop.run_until_complete(asyncio.wait(tasks)) |
| loop.close() |
| image_mapping = dict(zip(local_images, net_images)) |
| md = replace_md_img(md_path, image_mapping) |
| else: |
| print('无需上传图片') |
| return md |
| |
| |
| |
| |
| |
| |
| if __name__ == "__main__": |
| |
| if len(sys.argv) == 1: |
| exit(-1) |
| |
| file_or_dir_path = sys.argv[1] |
| if os.path.isfile(file_or_dir_path): |
| md_paths = [file_or_dir_path] |
| dir_name = os.path.dirname(file_or_dir_path) |
| else: |
| md_paths = get_md_files(file_or_dir_path) |
| dir_name = file_or_dir_path |
| |
| for md_path in md_paths: |
| md = read_md(md_path) |
| md = upload_img(md_path,md) |
| title, _ = os.path.splitext(os.path.basename(md_path)) |
| |
| post = dict(description=md, title=title, categories=['[Markdown]'],mt_keywords='anki') |
| server.metaWeblog.newPost(ST.blog_id ,ST.username , ST.password , post, ST.publish) |
| print(f"markdown上传成功, 博客标题为'{title}', 状态为'已发布', 请到博客园后台查看") |
| |
| time.sleep(3) |
| recent_posts = server.metaWeblog.getRecentPosts( ST.blog_id , ST.username,ST.password, 1) |
| |
| |
| out_md_path = os.path.join(dir_name,generate_file_name()) |
| |
| for post in recent_posts: |
| anki_title = post['title'] |
| link = post['link'] |
| content = post['description'].split('\n')[0] |
| write2csv(out_md_path,f'{anki_title}\t<a href={link}>{content}</a>\n') |
| break |
| |
| time.sleep(3) |
| |
| |
| |
cnblog_upload/src/file/md.py
| import os |
| |
| def get_md_files(folder_path): |
| markdown_files = [] |
| for file_name in os.listdir(folder_path): |
| if file_name.endswith(".md"): |
| markdown_files.append(os.path.join(folder_path, file_name)) |
| return markdown_files |
| |
| |
| |
| def read_md(path): |
| with open(path, encoding='utf-8') as f: |
| md = f.read() |
| return md |
| |
| |
cnblog_upload/src/file/csv.py
| from datetime import datetime |
| |
| |
| def generate_file_name(): |
| |
| now = datetime.now() |
| |
| time_str = now.strftime('%Y-%m-%d_%H-%M-%S') |
| |
| file_name = f'Z_{time_str}.csv' |
| return file_name |
| |
| |
| |
| |
| def write2csv(path,msg): |
| with open(path, "a", encoding='utf-8') as f: |
| f.write(msg) |
| |
| |
cnblog_upload/src/server/cnblog_server.py
| import xmlrpc.client |
| from conf.conf import ST |
| |
| try: |
| server = xmlrpc.client.ServerProxy(ST.blog_url) |
| except Exception as e: |
| e = str(e) |
| if 'unsupported XML-RPC protocol' in e: |
| print('请查看config.yaml文件中的blog_url,应该是这个URL地址没设置对') |
| |
cnblog_upload/src/img/mime.py
| mime_mapping = { |
| ".323": "text/h323", |
| ".asx": "video/x-ms-asf", |
| ".acx": "application/internet-property-stream", |
| ".ai": "application/postscript", |
| ".aif": "audio/x-aiff", |
| ".aiff": "audio/aiff", |
| ".axs": "application/olescript", |
| ".aifc": "audio/aiff", |
| ".asr": "video/x-ms-asf", |
| ".avi": "video/x-msvideo", |
| ".asf": "video/x-ms-asf", |
| ".au": "audio/basic", |
| ".application": "application/x-ms-application", |
| ".bin": "application/octet-stream", |
| ".bas": "text/plain", |
| ".bcpio": "application/x-bcpio", |
| ".bmp": "image/bmp", |
| ".cdf": "application/x-cdf", |
| ".cat": "application/vndms-pkiseccat", |
| ".crt": "application/x-x509-ca-cert", |
| ".c": "text/plain", |
| ".css": "text/css", |
| ".cer": "application/x-x509-ca-cert", |
| ".crl": "application/pkix-crl", |
| ".cmx": "image/x-cmx", |
| ".csh": "application/x-csh", |
| ".cod": "image/cis-cod", |
| ".cpio": "application/x-cpio", |
| ".clp": "application/x-msclip", |
| ".crd": "application/x-mscardfile", |
| ".deploy": "application/octet-stream", |
| ".dll": "application/x-msdownload", |
| ".dot": "application/msword", |
| ".doc": "application/msword", |
| ".dvi": "application/x-dvi", |
| ".dir": "application/x-director", |
| ".dxr": "application/x-director", |
| ".der": "application/x-x509-ca-cert", |
| ".dib": "image/bmp", |
| ".dcr": "application/x-director", |
| ".disco": "text/xml", |
| ".exe": "application/octet-stream", |
| ".etx": "text/x-setext", |
| ".evy": "application/envoy", |
| ".eml": "message/rfc822", |
| ".eps": "application/postscript", |
| ".flr": "x-world/x-vrml", |
| ".fif": "application/fractals", |
| ".gtar": "application/x-gtar", |
| ".gif": "image/gif", |
| ".gz": "application/x-gzip", |
| ".hta": "application/hta", |
| ".htc": "text/x-component", |
| ".htt": "text/webviewhtml", |
| ".h": "text/plain", |
| ".hdf": "application/x-hdf", |
| ".hlp": "application/winhlp", |
| ".html": "text/html", |
| ".htm": "text/html", |
| ".hqx": "application/mac-binhex40", |
| ".isp": "application/x-internet-signup", |
| ".iii": "application/x-iphone", |
| ".ief": "image/ief", |
| ".ivf": "video/x-ivf", |
| ".ins": "application/x-internet-signup", |
| ".ico": "image/x-icon", |
| ".jpg": "image/jpeg", |
| ".jfif": "image/pjpeg", |
| ".jpe": "image/jpeg", |
| ".jpeg": "image/jpeg", |
| ".js": "application/x-javascript", |
| ".lsx": "video/x-la-asf", |
| ".latex": "application/x-latex", |
| ".lsf": "video/x-la-asf", |
| ".manifest": "application/x-ms-manifest", |
| ".mhtml": "message/rfc822", |
| ".mny": "application/x-msmoney", |
| ".mht": "message/rfc822", |
| ".mid": "audio/mid", |
| ".mpv2": "video/mpeg", |
| ".man": "application/x-troff-man", |
| ".mvb": "application/x-msmediaview", |
| ".mpeg": "video/mpeg", |
| ".m3u": "audio/x-mpegurl", |
| ".mdb": "application/x-msaccess", |
| ".mpp": "application/vnd.ms-project", |
| ".m1v": "video/mpeg", |
| ".mpa": "video/mpeg", |
| ".me": "application/x-troff-me", |
| ".m13": "application/x-msmediaview", |
| ".movie": "video/x-sgi-movie", |
| ".m14": "application/x-msmediaview", |
| ".mpe": "video/mpeg", |
| ".mp2": "video/mpeg", |
| ".mov": "video/quicktime", |
| ".mp3": "audio/mpeg", |
| ".mpg": "video/mpeg", |
| ".ms": "application/x-troff-ms", |
| ".nc": "application/x-netcdf", |
| ".nws": "message/rfc822", |
| ".oda": "application/oda", |
| ".ods": "application/oleobject", |
| ".pmc": "application/x-perfmon", |
| ".p7r": "application/x-pkcs7-certreqresp", |
| ".p7b": "application/x-pkcs7-certificates", |
| ".p7s": "application/pkcs7-signature", |
| ".pmw": "application/x-perfmon", |
| ".ps": "application/postscript", |
| ".p7c": "application/pkcs7-mime", |
| ".pbm": "image/x-portable-bitmap", |
| ".ppm": "image/x-portable-pixmap", |
| ".pub": "application/x-mspublisher", |
| ".pnm": "image/x-portable-anymap", |
| ".png": "image/png", |
| ".pml": "application/x-perfmon", |
| ".p10": "application/pkcs10", |
| ".pfx": "application/x-pkcs12", |
| ".p12": "application/x-pkcs12", |
| ".pdf": "application/pdf", |
| ".pps": "application/vnd.ms-powerpoint", |
| ".p7m": "application/pkcs7-mime", |
| ".pko": "application/vndms-pkipko", |
| ".ppt": "application/vnd.ms-powerpoint", |
| ".pmr": "application/x-perfmon", |
| ".pma": "application/x-perfmon", |
| ".pot": "application/vnd.ms-powerpoint", |
| ".prf": "application/pics-rules", |
| ".pgm": "image/x-portable-graymap", |
| ".qt": "video/quicktime", |
| ".ra": "audio/x-pn-realaudio", |
| ".rgb": "image/x-rgb", |
| ".ram": "audio/x-pn-realaudio", |
| ".rmi": "audio/mid", |
| ".ras": "image/x-cmu-raster", |
| ".roff": "application/x-troff", |
| ".rtf": "application/rtf", |
| ".rtx": "text/richtext", |
| ".sv4crc": "application/x-sv4crc", |
| ".spc": "application/x-pkcs7-certificates", |
| ".setreg": "application/set-registration-initiation", |
| ".snd": "audio/basic", |
| ".stl": "application/vndms-pkistl", |
| ".setpay": "application/set-payment-initiation", |
| ".stm": "text/html", |
| ".shar": "application/x-shar", |
| ".sh": "application/x-sh", |
| ".sit": "application/x-stuffit", |
| ".spl": "application/futuresplash", |
| ".sct": "text/scriptlet", |
| ".scd": "application/x-msschedule", |
| ".sst": "application/vndms-pkicertstore", |
| ".src": "application/x-wais-source", |
| ".sv4cpio": "application/x-sv4cpio", |
| ".tex": "application/x-tex", |
| ".tgz": "application/x-compressed", |
| ".t": "application/x-troff", |
| ".tar": "application/x-tar", |
| ".tr": "application/x-troff", |
| ".tif": "image/tiff", |
| ".txt": "text/plain", |
| ".texinfo": "application/x-texinfo", |
| ".trm": "application/x-msterminal", |
| ".tiff": "image/tiff", |
| ".tcl": "application/x-tcl", |
| ".texi": "application/x-texinfo", |
| ".tsv": "text/tab-separated-values", |
| ".ustar": "application/x-ustar", |
| ".uls": "text/iuls", |
| ".vcf": "text/x-vcard", |
| ".wps": "application/vnd.ms-works", |
| ".wav": "audio/wav", |
| ".wrz": "x-world/x-vrml", |
| ".wri": "application/x-mswrite", |
| ".wks": "application/vnd.ms-works", |
| ".wmf": "application/x-msmetafile", |
| ".wcm": "application/vnd.ms-works", |
| ".wrl": "x-world/x-vrml", |
| ".wdb": "application/vnd.ms-works", |
| ".wsdl": "text/xml", |
| ".xap": "application/x-silverlight-app", |
| ".xml": "text/xml", |
| ".xlm": "application/vnd.ms-excel", |
| ".xaf": "x-world/x-vrml", |
| ".xla": "application/vnd.ms-excel", |
| ".xls": "application/vnd.ms-excel", |
| ".xof": "x-world/x-vrml", |
| ".xlt": "application/vnd.ms-excel", |
| ".xlc": "application/vnd.ms-excel", |
| ".xsl": "text/xml", |
| ".xbm": "image/x-xbitmap", |
| ".xlw": "application/vnd.ms-excel", |
| ".xpm": "image/x-xpixmap", |
| ".xwd": "image/x-xwindowdump", |
| ".xsd": "text/xml", |
| ".z": "application/x-compress", |
| ".zip": "application/x-zip-compressed", |
| ".*": "application/octet-stream", |
| } |
| |
cnblog_upload/src/img/unload_img.py
| |
| import os |
| import re |
| |
| from conf.conf import ST |
| from img.mime import mime_mapping |
| from server.cnblog_server import server |
| |
| |
| def find_md_img(md): |
| """查找markdown中的图片,排除网络图片(不用上传)""" |
| images = re.findall("!\\[.*?\\]\\((.*)\\)", md) |
| images += re.findall('<img src="(.*?)"', md) |
| images = [i for i in images if not re.match("((http(s?))|(ftp))://.*", i)] |
| print(f'共找到{len(images)}张本地图片{images}') |
| return images |
| |
| |
| async def upload_md_img(path): |
| """上传图片""" |
| name = os.path.basename(path) |
| _, suffix = os.path.splitext(name) |
| with open(path, 'rb') as f: |
| file = { |
| "bits": f.read(), |
| "name": name, |
| "type": mime_mapping[suffix] |
| } |
| url = server.metaWeblog.newMediaObject(ST.blog_id,ST.username ,ST.password, file) |
| return url |
| |
| |
| def replace_md_img(path, img_mapping): |
| """替换markdown中的图片链接""" |
| with open(path, 'r', encoding='utf-8') as fr: |
| md = fr.read() |
| for local, net in img_mapping.items(): |
| md = md.replace(local, net) |
| if ST.img_format: |
| md_links = re.findall("!\\[.*?\\]\\(.*?\\)", md) |
| md_links += re.findall('<img src=.*/>', md) |
| for ml in md_links: |
| img_url = re.findall("!\\[.*?\\]\\((.*?)\\)", ml) |
| img_url += re.findall('<img src="(.*?)"', ml) |
| img_url = img_url[0] |
| |
| if ST.img_format == "typora": |
| zoom = re.findall(r'style="zoom:(.*)%;"', ml) |
| if zoom: |
| md = md.replace(ml, f'<center><img src="{img_url}" style="width:{zoom[0]}%;" /></center>') |
| else: |
| md = md.replace(ml, ST.img_format.format(img_url)) |
| |
| if ST.gen_network_file: |
| path_net = os.path.join(os.path.dirname(path), '_network'.join(os.path.splitext(os.path.basename(path)))) |
| with open(path_net, 'w', encoding='utf-8') as fw: |
| fw.write(md) |
| print(f'图片链接替换完成,生成新markdown:{path_net}') |
| return md |
| |
cnblog_upload/src/conf/conf.py
| |
| class ST: |
| blog_url="https://rpc.cnblogs.com/metaweblog/xxxxx" |
| blog_id="xxxxxxx" |
| username="xxxxxxxxxxxxx" |
| password="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
| |
| gen_network_file=False |
| |
| publish=True |
| |
| img_format="typora" |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战