medup
Version, currently 0.8.020 versions
- 0.8.0latestJul 27, 2022
- 0.7.0not indexedAug 18, 2022
- 0.6.0not indexedAug 18, 2022
- 0.5.0not indexedAug 18, 2022
- 0.4.1not indexedAug 18, 2022
- 0.4.0not indexedAug 18, 2022
- 0.3.0not indexedAug 18, 2022
- 0.2.1not indexedAug 18, 2022
- 0.2.0not indexedAug 18, 2022
- 0.1.10not indexedAug 18, 2022
- 0.1.9not indexedAug 18, 2022
- 0.1.8not indexedAug 18, 2022
- 0.1.7not indexedAug 18, 2022
- 0.1.6not indexedAug 18, 2022
- 0.1.5not indexedAug 18, 2022
- 0.1.4not indexedAug 18, 2022
- 0.1.3not indexedAug 18, 2022
- 0.1.2not indexedAug 18, 2022
- 0.1.1not indexedAug 18, 2022
- 0.1.0not indexedAug 18, 2022
github.com/miry/medup
Download Medium and Dev.to content to local folder.
36 stars
0 dependents
License: LGPL-3.0
Installation
# Add this to your shard.yml
dependencies:
medup:
github: miry/medup
version: ~> 0.8.0Then run:
shards installshard.yml
- Crystal
1.5.0- License
- LGPL-3.0
- Author
- Michael Nikitochkin <michael.nikitochkin@gmx.net>
- Target
medupfrom src/cli.cr
Dependencies
Runtime Dependencies
- json_mapping*github: crystal-lang/json_mapping.cr
- zaru_crystal*github: szTheory/zaru_crystal, branch: master
Development Dependencies
- webmock*github: manastech/webmock.cr, branch: masterdev
README
[](https://stand-with-ukraine.pp.ua/)

# Medup
[](https://github.com/miry/medup/releases)
[](https://raw.githubusercontent.com/miry/medup/master/LICENSE)
[](https://stand-with-ukraine.pp.ua)
[](https://stand-with-ukraine.pp.ua)
> Sync all content from Medium or Dev.to with local folder via API
# Table of Contents
* [Usage](#usage)
* [Features](#features)
* [Getting Started](#getting-started)
* [Contributing](#contributing)
* [Contributors](#contributors)
* [License](#license)
# Usage
## Export post
Export a single medium post to local
```shell
medup https://medium.com/notes-and-tips-in-full-stack-development/medup-backups-articles-8bf90179b094
```
Export a single dev.to post to local
```shell
medup https://dev.to/jetthoughts/the-trial-period-for-staff-augmentation-in-jetthoughts-1h5c
```
## Author's posts
Export all articles written by single author to special folder `posts/miry`
```shell
medup -u miry -d posts/miry
```
Alternative:
```shell
medup @miry -d posts/miry
```
Similar for Dev.to
```shell
medup --platform=devto @miry
medup --platform=devto -u miry
medup https://dev.to/miry
```
## User's recommendations
Export all articles recommended by `miry`:
```shell
medup -u miry -d posts/recommendations -r
```
Alternative:
```shell
medup @miry -d posts/recommendations -r
```
## Publication's posts
Export all articles written by single author to special folder `posts/miry`
```shell
medup -p jetthoughts -d posts/jetthoughts
```
Alternative:
```shell
medup jetthoughts -d posts/jetthoughts
```
Similar for Dev.to
```shell
medup --platform=devto jetthoughts
medup --platform=devto -p jetthoughts
medup https://dev.to/jetthoughts
```
## Assets
By default all images are embedded in the article.
It is possible to save images in separate folder with arguments - `--assets-images`
```shell
$ medup @miry --assets-images -d posts/miry
$ $ tree posts/miry
posts/miry
├── 2017-03-20-war-with-ads-and-trackers.md
├── 2017-03-26-setup-k8s-v1-6-0-rc-1-cluster.md
├── 2017-03-28-docker-network-performance.md
...
└── assets
├── 0*BmauN6EER-PdvCee.jpeg
├── 0*FbFs8aNmqNLKw4BM.jpeg
├── 0*LZaURw4xtfA74nu9.jpeg
...
```
### Specify assets directory
To make it is even more agile and work with Jekyl, put assets in different directory - `--assets-dir=DIR`
```shell
$ medup @miry --assets-images -d site/posts --assets-dir=site/assets
$ tree site
site
├── assets
│ ├── 0*BmauN6EER-PdvCee.jpeg
│ ├── 0*FbFs8aNmqNLKw4BM.jpeg
│ ├── 0*LZaURw4xtfA74nu9.jpeg
...
└── posts
├── 2017-03-20-war-with-ads-and-trackers.md
├── 2017-03-26-setup-k8s-v1-6-0-rc-1-cluster.md
├── 2017-03-28-docker-network-performance.md
...
```
### Specify assets base path
Also change the path in the document to find assets with `--assets-base-path=URI`.
Default base path is `./assets/ASSET_NAME.XXX`. Example image: ``.
With `--assets-base-path=/static` the src would be `/static/ASSET_NAME.XXX`.
Example image: ``.
```shell
$ medup @miry --assets-images -d tmp/posts --assets-dir=tmp/static --assets-base-path=/static
```
## ENVIRONMENT
* **MEDUP_GITHUB_API_TOKEN**
Use this GitHub personal access token when accessing the GitHub Gist.
## Mix
The neat feature to mix mutliple rules in single command
```shell
medup -u miry @pftg -p jetthoughts notes-and-tips-in-full-stack-development \
https://medium.com/notes-and-tips-in-full-stack-development/medup-backups-articles-8bf90179b094 \
https://dev.to/jetthoughts \
https://dev.to/jetthoughts/the-trial-period-for-staff-augmentation-in-jetthoughts-1h5c
```
It would download all articles for:
1. user `@miry`
1. user `@pftg`
1. publication `jetthoughts`
1. publication `notes-and-tips-in-full-stack-development`
1. single post "Medup backups articles"
1. publication `jetthoughts` from dev.to
1. single post "The Trial Period for Staff Augmentation in JetThoughts" from dev.to
# Features
* Discover all articles from user account available in public
* Allow to download all recommended articles by user
* Discover all articles from publisher, that available in public
* Download images used inside article or in `assets` folder
* Save posts in markdown format
* Convert a single article in markdown format
* Extract gists and convert to code blocks
# Getting Started
## Homebrew
Install `medup` tool to the MacOS or Linux via Hombrew:
```shell
$ brew tap miry/medup
$ brew install medup
```
Run export command for **Medium** author *miry* and save articles to local folder:
```shell
$ medup -u miry -d .
$ medup -d . https://medium.com/notes-and-tips-in-full-stack-development/medup-backups-articles-8bf90179b094
```
## Docker
Docker way to make same job:
```shell
$ docker run -v <path to local articles folder>:/posts -it miry/medup -u <user>
```
Download all articles that user has recommended:
```shell
$ docker run -v <path to local articles folder>:/posts -it miry/medup -u <user> --recommended -d posts/recommended
```
## Crystal
Run dumping with source code
```shell
$ shards install
$ crystal run src/cli.cr -- -u <medium user> -d <destination folder>
```
Example download all posts from author https://medium.com/@miry to local folder `posts/miry`
```shell
$ crystal run src/cli.cr -- -u miry -d posts/miry
```
## Build
Build a application binary and execute:
```shell
$ shards install
$ rake build
$ _output/medup -u <medium user> -d <destination folder>
```
# Result
By default all articles destination folder is `posts`.
In the directory, you can find 2 format of files: `.json` and `.md`.
- *JSON* format is the raw, what *Medium* returns.
- *Markdown* format is simple implementation of block formated text.
Images encoded in the result document.
Rich elements content are located in `posts/assets` and integrated with `<IFRAME>`.
## Markdown
Example of exported document.
1. Original Medium post vs Exported Markdown:

1. Exported Markdown in Github pages vs original Dev.to

## Demo
There are possible solution to test export markdown files.
Run a test Jekyll server with posts. Example:
```shell
$ rake demo:serve
```
And after it finishes you can test http://localhost:4000.
Update posts for demo:
```shell
$ medup -v7 -d demo/_posts/ --assets-dir=demo/assets --assets-base-path=/assets <url>|<@user>|<publisher>
```
Test Bridgetown use next command:
```shell
$ rake demo:bridgetown:serve
```
Bridgetown has a bit different structure for posts. Use to update posts:
```shell
$ medup -v7 -d demo/src/_posts/ --assets-dir=demo/src/assets --assets-base-path=/assets <url>|<@user>|<publisher>
```
# Contributing
1. Fork it ( https://github.com/miry/medup/fork )
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create a new Pull Request
# Contributors
- [@miry](https://github.com/miry) Michael Nikitochkin - creator, maintainer
- [@alexanderadam](https://github.com/alexanderadam) Alexander - supporter
- [@clawfire](https://github.com/clawfire) Thibault Milan - supporter
# License
This project is under the LGPL-3.0 license.
# Play ownself
### FAQ
* Clean unused assets:
```shell
$ for i in $(ls -1 assets/*); do grep -q "$i" *.md || echo $i; done | xargs rm
```
* Detect potential articles with **IFRAME** content for gists:
```shell
$ for i in $(ls -1 assets/*.html); do grep "gist.github.com" -q $i && grep -n "$i" *.md; done
```
* Detect potential articles' url with **IFRAME** content for gists:
```shell
$ for i in $(ls -1 assets/*.html); do grep "gist.github.com" -q $i && (grep -l "$i" *.md | xargs head | grep url); done
```
* Detect potential articles' url with **IFRAME** content for youtube:
```shell
$ for i in $(ls -1 assets/*.html); do grep "schema=youtube" -q $i && (grep -l "$i" *.md | xargs head | grep url); done
```
### Medium API
1. [Medium API documentation](https://github.com/Medium/medium-api-docs)
1. Generate token on the page https://medium.com/me/settings
1. Create environment variable `MEDIUM_TOKEN=<token>`
1. Verify token with sample query:
```shell
$ curl -H "Authorization: Bearer <token>" https://api.medium.com/v1/me
{"data":{"id":"number","username":"miry","name":"Michael Nikitochkin","url":"https://medium.com/@miry","imageUrl":"https://cdn-images-1.medium.com/fit/c/400/400/0*KgbjgGnH-csHuB8j."}}
```
### Crawler
1. Check public information not covered by API
```shell
$ curl "https://medium.com/@miry?format=json" | cut -c17- && : Remove in the front from response some strange JS code.
```
2. Pagination
```shell
$ curl "https://medium.com/@miry/latest?format=json&limit=100" | cut -c17- && : Remove in the front from response some strange JS code.
```
3. Post info
```shell
curl -s -H "Content-Type: application/json" https://medium.com/@miry/c35b40c499e\?format\=json\&limit\=100
```
4. Stream
```shell
$ curl -s -H "Content-Type: application/json" "https://medium.com/_/api/users/fdf238948af6/profile/stream?source=overview" | cut -c17-
$ curl -s -H "Content-Type: application/json" "https://medium.com/_/api/users/fdf238948af6/profile/stream?limit=100&page=3" | cut -c17- > stream.json
$ cat stream.json| jq ".payload.references.Post[].title"
$ cat stream.json| jq ".payload.paging.next"
```
5. Recommendations
```shell
$ curl -s -H "Content-Type: application/json" https://medium.com/@miry/has-recommended | cut -c17-
$ curl -s -H "Content-Type: application/json" "https://medium.com/_/api/users/fdf238948af6/profile/stream?source=has-recommended" | cut -c17-
```
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.8.0- Tagged
- Jul 27, 2022
- Commit
a79e1523259f- Crystal
1.5.0- Indexed
- yes
Dependents
No indexed shard depends on this one yet.
Repository
github.com/miry/medup
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 12, 2026
- Synced
- Aug 12, 2026
- Versions
- 20