[![Ruby](https://github.com/thedrummeraki/mangadex/actions/workflows/ruby.yml/badge.svg)](https://github.com/thedrummeraki/mangadex/actions/workflows/ruby.yml) # Mangadex Welcome to `mangadex`, your next favourite Ruby gem for interacting with [Mangadex](https://mangadex.org). ## Important information **By using this gem you accept**: - To **credit [Mangadex](https://mangadex.org)**. This gem is your friendly neighbourhood wrapper on _their_ API. - To **credit scanlation groups**, especially if you offer the ability to read chapters. - **Not to run any ads** on the service that will use this gem. Please do not make money off of Mangadex's services. These are Mangadex's [rules](https://api.mangadex.org/docs.html#section/Acceptable-use-policy), please follow them. ## Installation Add this line to your application's Gemfile: ```ruby gem 'mangadex' ``` And then execute: $ bundle Or install it yourself as: $ gem install mangadex ## Usage Please note that I tried my best to follow Mangadex's naming conventions for [their documentation](https://api.mangadex.org). Track the progress [here in an issue](https://github.com/thedrummeraki/mangadex/issues/5). Although a work a in progress, feel free to [check this out](lib/mangadex). ### Basic Usage Here's a couple of cool things you can do with the gem: #### Get a list of manga ```ruby response = Mangadex::Manga.list manga = response.data # Array of # ``` #### Get a manga by id, with cover_art ```ruby manga_id = 'd86cf65b-5f6c-437d-a0af-19a31f94ec55' response = Mangadex::Manga.get(manga_id, includes: :cover_art) entity = response.data # Object of # # Original size entity.cover_art.image_url(size: :original) entity.cover_art.image_url(size: :medium) entity.cover_art.image_url(size: :small) # default size ``` #### Get a manga's chapter list, ordered by volume and chapter number ```ruby manga_id = 'd86cf65b-5f6c-437d-a0af-19a31f94ec55' manga_response = Mangadex::Manga.get(manga_id, includes: :cover_art) entity = manga_response.data chapter_response = Mangadex::Chapter.list( manga: entity.id, order: { volume: 'asc', chapter: 'asc' }, translated_language: 'en', ) chapters = chapter_response.data # Array of # ``` #### Get a chapter's pages ```ruby chapter_id = 'e7bb1892-7f83-4a89-bccc-0d6d403a85fc' chapter = Mangadex::Chapter.get(chapter_id).data pages = chapter.page_urls # Data saver true by default ``` #### Search for manga by title ```ruby response = Mangadex::Manga.list(title: 'Ijiranaide nagatoro') found_manga = response.data ``` #### Authenticate with a block...: ```ruby Mangadex::Auth.login(username: 'username', password: 'password') do |user| # `user` is of type Mangadex::Api::User puts(user.mangadex_user_id) puts(user.session) puts(user.refresh) puts(user.session_valid_until) end ``` ...or inline...: ```ruby # `user` is of type Mangadex::Api::User user = Mangadex::Auth.login(username: 'username', password: 'password') puts(user.mangadex_user_id) puts(user.session) puts(user.refresh) puts(user.session_valid_until) ``` You can access the authenticated user by using context: ```ruby user = Mangadex.context.user ``` #### Refresh the user's token ```ruby Mangadex.context.user.refresh_session! do |user| # `user` is of type Mangadex::Api::User puts(user.mangadex_user_id) puts(user.session) puts(user.refresh) puts(user.session_valid_until) end ``` #### Create an public MDList, add then remove a manga ```ruby Mangadex::Auth.login(...) response = Mangadex::CustomList.create(name: 'My awesome list!', visibility: 'public') custom_list = response.data manga_id = 'd86cf65b-5f6c-437d-a0af-19a31f94ec55' # Add the manga custom_list.add_manga(manga_id) # Remove the manga custom_list.remove_manga(manga_id) # Get manga list manga = custom_list.manga_details.data # Array of # ``` ### Are you on Rails? This gem tries its best to be agnostic to popular frameworks like Rails. Here's however a couple of things to can do to integrate the gem to your project. First, add the gem to your `Gemfile`. #### Configurating the gem Create a initilizer file to `config/initializers/mangadex.rb`. You can add the following: ```ruby Mangadex.configure do |config| # Override the default content ratings config.default_content_ratings = %w(safe suggestive) # Override the Mangadex API URL (ie: proxy) config.mangadex_url = 'https://my-proxy-mangadex-url.com' end ``` #### Authenticate your users This could be useful if you want to support authentication. You will need to persist your user's session, refresh token and session expiry date. ##### Persist the user information If you haven't done so, create your user. ```ruby class CreateUsers < ActiveRecord::Migration[6.1] def change create_table :users do |t| t.string :mangadex_user_id, null: false t.string :username t.string :session t.string :refresh t.datetime :session_valid_until t.timestamps end end end ``` Already created your `User` class? Make sure it has all of the following: - `mangade_user_id`: ID used to identify your user on Mangadex - `username`: Your username - `session`: The session token (valid for 15 minutes) - `refresh`: The refresh token, used to refresh the session (valid for 1 month) - `session_valid_until`: The time `session` session expires at If anything is missing, create a migration. #### Authentication flow on the controller Add these methods to your controller's helper (ie: `ApplicationHelper`): ```ruby module ApplicationHelper def current_user @current_user ||= User.find_by(id: session[:id]) end def logged_in? current_user.present? end def log_in(user) # `user` is an instance of your `User` class session[:id] = user.id6 end def log_out # Logout from Mangadex Mangadex::Auth.logout # Remove the session session.delete(:id) end end ``` First make sure `ApplicationController` includes the helper above ```ruby class ApplicationController < ActionController::Base include ApplicationHelper # ... end ``` We recommend creating a controller for authentication. Here's how you can implement the login and logout actions: ```ruby # app/controllers/session_controller.rb class SessionController < ApplicationController # GET /login def new # render the login form end # POST /login def create username = params[:username] password = params[:password] # You can also use `email` instead of `username` to log in user = Mangadex::Auth.login(username: username, password: password) do |mangadex_user| # Find the user by mangadex user id (or initialize if it doesn't exist) our_user = User.find_or_initialize_by(mangadex_user_id: mangadex_user.mangadex_user_id) do |new_user| new_user.username = mangadex_user.data.username end # Update the session info data our_user.session = mangadex_user.session our_user.refresh = mangadex_user.refresh our_user.session_valid_until = mangadex_user.session_valid_until # ...then save the user our_user.save! our_user end # `user` will be an instance of your `User` class # Now, we can log in then redirect to the root. log_in(user) redirect_to(root_path) rescue Mangadex::Errors::AuthenticationError => error # See https://api.mangadex.org/docs.html to learn more about errors Rails.logger.error(error.response.errors) # Handle authentication errors here end # DELETE /logout def destroy log_out redirect_to(root_path) end end ``` Finally, add the routes. ```ruby # config/routes.rb Rails.application.routes.draw do # ... get '/login' => 'session#new' post '/login' => 'session#create' delete '/logout' => 'session#destroy' end ``` #### Protected resources Here's an example of a controller that requires every action to be logged in. This is based on the steps above. ```ruby class ProtectedController < ApplicationController before_action :ensure_logged_in! private def ensure_logged_in! return if logged_in? redirect_to(login_path) # go to /login end end ``` We're going with managing (list, create, show, edit, delete) MDLists (ie: custom lists). __We're not using strong params below to keep things simple, but you should, especially when mutating data (ie: creating and editing)__. ```ruby class CustomListsController < ProtectedController # GET /custom_list def index @custom_lists = Mangadex::CustomList.list end # GET /custom_list/new def new # new custom list form end # POST /custom_list def create @custom_list = Mangadex::CustomList.create( name: params[:name], visibility: params[:visibility], manga: params[:manga], # Manga ID ) end # GET /custom_list/ def show @custom_list = Mangadex::CustomList.get(params[:id]) end # GET /custom_list//edit def edit @custom_list = Mangadex::CustomList.get(params[:id]) # edit custom list form end # PUT /custom_list/ # PATCH /custom_list/ def update # Note: when updating the custom list, be sure to pass in # the current version number! @custom_list = Mangadex::CustomList.update( params[:id], { name: params[:name], visibility: params[:visibility], manga: params[:manga], version: params[:version].to_i, } ) end # DELETE /custom_list/ def destroy Mangadex::CustomList.delete(params[:id]) end end ``` ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/thedrummeraki/mangadex. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). ## Code of Conduct Everyone interacting in the Mangadex project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/thedrummeraki/mangadex/blob/master/CODE_OF_CONDUCT.md).