diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..8f1789b --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,8 @@ +# On Windows MSVC, statically link the C runtime so that the resulting EXE does +# not depend on the vcruntime DLL. +# +# See: https://github.com/BurntSushi/ripgrep/pull/1613 +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] +[target.i686-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bd713b5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,238 @@ +# The way this works is the following: +# +# The create-release job runs purely to initialize the GitHub release itself +# and to output upload_url for the following job. +# +# The build-release job runs only once create-release is finished. It gets the +# release upload URL from create-release job outputs, then builds the release +# executables for each supported platform and attaches them as release assets +# to the previously created release. +# +# The key here is that we create the release only once. +# +# Reference: +# https://eugene-babichenko.github.io/blog/2020/05/09/github-actions-cross-platform-auto-releases/ + +name: Github Release +on: + push: + # Enable when testing release infrastructure on a branch. + # branches: + # - master + tags: + - "[0-9]+.[0-9]+.[0-9]+" +jobs: + create-release: + name: create-release + runs-on: ubuntu-latest + # env: + # # Set to force version number, e.g., when no tag exists. + # RG_VERSION: TEST-0.0.0 + outputs: + upload_url: ${{ steps.release.outputs.upload_url }} + rg_version: ${{ env.RG_VERSION }} + steps: + - name: Get the release version from the tag + shell: bash + if: env.RG_VERSION == '' + run: | + # Apparently, this is the right way to get a tag name. Really? + # + # See: https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027 + echo "RG_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + echo "version is: ${{ env.RG_VERSION }}" + - name: Create GitHub release + id: release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.RG_VERSION }} + release_name: ${{ env.RG_VERSION }} + + build-release: + name: build-release + needs: ["create-release"] + runs-on: ${{ matrix.os }} + env: + # For some builds, we use cross to test on 32-bit and big-endian + # systems. + CARGO: cargo + # When CARGO is set to CROSS, this is set to `--target matrix.target`. + TARGET_FLAGS: "" + # When CARGO is set to CROSS, TARGET_DIR includes matrix.target. + TARGET_DIR: ./target + # Emit backtraces on panics. + RUST_BACKTRACE: 1 + BINARY: "orly" + PKG_CONFIG_ALL_STATIC: "true" + PKG_CONFIG_PATH: "/usr/local/opt/libxml2/lib/pkgconfig" + MACOSX_DEPLOYMENT_TARGET: "10.7" + strategy: + matrix: + build: [macos, win-msvc] + include: + # - build: linux-arm + # os: ubuntu-18.04 + # rust: nightly + # target: arm-unknown-linux-gnueabihf + # - build: win32-msvc + # os: windows-2019 + # rust: nightly + # target: i686-pc-windows-msvc + - build: macos + os: macos-latest + rust: nightly + target: x86_64-apple-darwin + - build: win-msvc + os: windows-2019 + rust: nightly + target: x86_64-pc-windows-msvc + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 1 + + - name: Install dependencies (macOS) + if: matrix.os == 'macos-latest' + run: | + brew install libxml2 zlib + + # pkg-config-rs hack to force static linking for zlib + ln -s $(brew --prefix)/opt/zlib/include/zlib.h $(brew --prefix)/opt/libxml2/include/zlib.h + ln -s $(brew --prefix)/opt/zlib/lib/libz.a $(brew --prefix)/opt/libxml2/lib/libz.a + + - name: Restore from cache and install vcpkg (Windows x64) + if: matrix.os == 'windows-2019' + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: "5568f110b509a9fd90711978a7cb76bae75bb092" + + - name: Install dependencies (Windows x64) + shell: bash + if: matrix.os == 'windows-2019' + run: | + # Using static libxml version along with "crt-static" rustflags + # in .cargo/config/toml allows building static binaries + $VCPKG_ROOT/vcpkg install libxml2:x64-windows-static + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + target: ${{ matrix.target }} + + - name: Use Cross + shell: bash + run: | + cargo install cross + echo "CARGO=cross" >> $GITHUB_ENV + echo "TARGET_FLAGS=--target ${{ matrix.target }}" >> $GITHUB_ENV + echo "TARGET_DIR=./target/${{ matrix.target }}" >> $GITHUB_ENV + + - name: Show command used for Cargo + run: | + echo "cargo command is: ${{ env.CARGO }}" + echo "target flag is: ${{ env.TARGET_FLAGS }}" + echo "target dir is: ${{ env.TARGET_DIR }}" + + - name: Build release binary + shell: bash + run: | + # Required for windows builds + export PATH=$PATH:${{env.VCPKG_ROOT}} + ${{ env.CARGO }} +nightly build ${{ env.TARGET_FLAGS }} --release --locked + + - name: Build archive + shell: bash + run: | + staging="$BINARY-${{ needs.create-release.outputs.rg_version }}-${{ matrix.target }}" + + if [ "${{ matrix.os }}" = "windows-2019" ]; then + cp "target/${{ matrix.target }}/release/$BINARY.exe" "$BINARY.exe" + 7z a "$staging.zip" "$BINARY.exe" + echo "ASSET=$staging.zip" >> $GITHUB_ENV + else + cp "target/${{ matrix.target }}/release/$BINARY" "$BINARY" + strip "$BINARY" + tar -czvf "$staging.tar.gz" "$BINARY" + echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV + fi + + - name: Upload release archive + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} + asset_path: ${{ env.ASSET }} + asset_name: ${{ env.ASSET }} + asset_content_type: application/octet-stream + # Building musl version requires all dependencies to be built with musl too. + # There are some issues when building musl version of libxml with cross + # (specifically with openssl). clux/muslrust has musl-compiled version of openssl + # which solves the issue + + build-release-musl: + needs: ["create-release"] + runs-on: ubuntu-latest + container: clux/muslrust:nightly + env: + LIBXML_VER: "2.9.12" + BINARY: "orly" + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Link to predefined musl toolchain + run: | + ln -s /root/.cargo $HOME/.cargo + ln -s /root/.rustup $HOME/.rustup + + - name: Compile libxml2 against musl + run: | + curl -sSL ftp://xmlsoft.org/libxml2/libxml2-$LIBXML_VER.tar.gz | tar xz + cd libxml2-$LIBXML_VER + PREFIX=/musl CC="musl-gcc -fPIC -pie" LDFLAGS="-L$PREFIX/lib" CFLAGS="-I$PREFIX/include" \ + ./configure --with-lzma=no --prefix=$PREFIX --host=x86_64-unknown-linux-musl + make -j$(nproc) + make install + + - name: Build release binary + run: | + cargo +nightly build --release --locked + + - name: Build archive + shell: bash + run: | + ARCHIVE="$BINARY-${{ needs.create-release.outputs.rg_version }}-x86_64-unknown-linux-musl.tar.gz" + cp "target/x86_64-unknown-linux-musl/release/$BINARY" "$BINARY" + strip "$BINARY" + tar -czvf "$ARCHIVE" "$BINARY" + echo "ASSET=$ARCHIVE" >> $GITHUB_ENV + + - name: Upload release archive + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} + asset_path: ${{ env.ASSET }} + asset_name: ${{ env.ASSET }} + asset_content_type: application/octet-stream + + publish-crate: + name: publish-crate + runs-on: ubuntu-latest + needs: ["build-release", "build-release-musl"] + steps: + - uses: actions/checkout@v1 + - run: cargo login ${CRATES_IO_TOKEN} + env: + CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} + - run: cargo publish diff --git a/Cargo.lock b/Cargo.lock index 31f89fc..ea87fe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -839,6 +839,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +[[package]] +name = "openssl-src" +version = "111.16.0+1.1.1l" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab2173f69416cf3ec12debb5823d244127d23a9b127d5a5189aa97c5fa2859f" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.65" @@ -848,6 +857,7 @@ dependencies = [ "autocfg", "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index 24913d3..e25537e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,21 @@ version = "0.1.0" edition = "2018" authors = ["hurlenko"] description = "Download O'Reilly books as EPUB" -license = "MIT" +license-file = "LICENSE" +repository = "https://github.com/hurlenko/orly" +readme = "README.md" +exclude = [ + ".github/*", +] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +opt-level = "z" # Optimize for size. +lto = true +codegen-units = 1 +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -reqwest = { version = "0.11.4", features = ["json", "cookies", "gzip"] } +reqwest = { version = "0.11.4", features = ["json", "cookies", "gzip", "native-tls-vendored"] } url = "2.2.2" tokio = { version = "1", features = ["full"] } serde = "1.0.126" diff --git a/README.md b/README.md new file mode 100644 index 0000000..1831abb --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# orly + +Download O'Reilly books as EPUB. + +![GitHub release](https://img.shields.io/github/v/release/hurlenko/orly) +![Downloads](https://img.shields.io/github/downloads/hurlenko/orly/latest/total) +![Crates.io](https://img.shields.io/crates/d/orly) +![License](https://img.shields.io/badge/license-MIT-blue.svg) + +## Table of Contents + +- [Installation](#installation) +- [Usage](#usage) +- [Command line interface](#command-line-interface) + +## Installation + +- **[Archives of precompiled binaries for orly are available for Windows, +macOS and Linux.](https://github.com/hurlenko/orly/releases)** Linux and +Windows binaries are static executables. + +- If you're a **Rust programmer**, orly can be installed with `cargo`. + + > Note that the minimum supported version of Rust for `orly` is **1.54.0**. + + ```bash + cargo install orly + ``` + +After installation, the `orly` command will be available. Check the [command line](#command-line-interface) section for supported commands. + +## Usage + +- You will need an O'Reily account with a non-expired subscription. + +- Find the book you want to download and copy its id (the digits at the end of the url). + +- Use your credentials to download the book: + + ```bash + orly --creds "email@example.com" "password" 1234567890 + ``` + +## Command line interface + +Currently `orly` supports these commands + +```bash +USAGE: + orly.exe [FLAGS] [OPTIONS] --creds ... + +ARGS: + Book ID to download. Digits from the URL + +FLAGS: + -h, --help Print help information + -k, --kindle Tweak css to avoid overflow. Useful for e-readers + -v, --verbose Sets the level of verbosity + -V, --version Print version information + +OPTIONS: + -c, --creds Sign in credentials + -o, --output Directory to save the final epub to [default: .] + -t, --threads Sets the maximum number of concurrent http requests [default: 20] +``` diff --git a/src/main.rs b/src/main.rs index a6a5083..487c05e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,7 +90,7 @@ async fn run(cli_args: &CliArgs) -> Result<()> { info!("Getting book info"); let book = client.fetch_book_details(book_id).await?; - info!("Title: {}", book.title); + info!("Title: {:?}", book.title); info!( "Authors: {:?}", book.authors @@ -113,7 +113,7 @@ async fn run(cli_args: &CliArgs) -> Result<()> { .await .context("Unable to create file")?; let toc = client.fetch_toc(book_id).await?; - info!("Downloaded toc: {}", toc.len()); + info!("Toc size: {}", toc.len()); EpubBuilder::new(&book, cli_args.kindle)? .chapters(&chapters)? @@ -148,13 +148,12 @@ fn set_up_logging(verbosity: u8) { base_config .format(move |out, message, record| { out.finish(format_args!( - "{color_line}[{date}][{target}][{level}{color_line}] {message}\x1B[0m", + "{color_line}[{date}][{level}{color_line}] {message}\x1B[0m", color_line = format_args!( "\x1B[{}m", colors_line.get_color(&record.level()).to_fg_str() ), date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), - target = record.target(), level = colors_level.color(record.level()), message = message, )); diff --git a/src/models.rs b/src/models.rs index 59645c1..be4476c 100644 --- a/src/models.rs +++ b/src/models.rs @@ -36,12 +36,7 @@ pub(crate) struct BillingInfo { #[derive(Deserialize, Debug)] pub(crate) struct Credentials { - id_token: String, - refresh_token: String, pub logged_in: bool, - expires_at: String, - redirect_uri: String, - uuid: String, } #[derive(Deserialize, Debug)]