packages feed

pixiv-0.1.1: src/Web/Pixiv/Types.hs

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Copyright: (c) 2021 The closed eye of love
-- SPDX-License-Identifier: BSD-3-Clause
-- Maintainer: Poscat <poscat@mail.poscat.moe>, berberman <berberman@yandex.com>
-- Stability: alpha
-- Portability: portable
-- Data types used in API. This module enables [DuplicateRecordFields](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#extension-DuplicateRecordFields),
-- so please consider using "Web.Pixiv.Types.Lens" to access fields smoothly.
module Web.Pixiv.Types
  ( -- * ImageUrl
    ImageUrl,
    ImageUrls (..),
    OriginalImageUrl (..),

    -- * Tag
    Tag (..),
    TrendingTag (..),
    TrendingTags (..),

    -- * Illustration
    Series (..),
    IllustType (..),
    MetaPage (..),
    Illust (..),
    Illusts (..),
    IllustWrapper (..),

    -- * User
    User (..),
    UserProfile (..),
    Publicity (..),
    ProfilePublicity (..),
    Workspace (..),
    UserDetail (..),
    UserPreview (..),
    UserPreviews (..),

    -- * Comment
    Comment (..),
    Comments (..),

    -- * NextUrl
    NextUrlLess,
    HasNextUrl (..),

    -- * Ugoria
    UgoiraFrame (..),
    ZipUrls (..),
    UgoiraMetadata (..),
    UgoiraMetadataWrapper (..),

    -- * Article
    SpotlightArticle (..),
    SpotlightArticles (..),

    -- * Http
    RankMode (..),
    SearchTarget (..),
    SortingMethod (..),
    Duration (..),
  )
where

import qualified Data.Aeson as A
import Data.Text (Text)
import Data.Time (UTCTime)
import Web.Pixiv.TH

-- | Undecorate @next_url@ of a type.
--
-- @next_url@ is returned by some APIs for paging.
type family NextUrlLess a

-- | Class to get or unwrap @next_url@.
class HasNextUrl a where
  unNextUrl :: a -> NextUrlLess a
  getNextUrl :: a -> Maybe Text

-----------------------------------------------------------------------------

-- | Image urls are represented in 'Text'.
type ImageUrl = Text

-- | An object contains image urls.
--
-- * Example in 'Illust' or 'MetaPage':
--   @
--     "image_urls": {
--       "square_medium": "...",
--       "medium": "...",
--       "large": "..."
--     }
--   @
-- * Example in 'User':
--   @
--     "profile_image_urls": {
--       "medium": "..."
--     }
--   @
data ImageUrls = ImageUrls
  { _squareMedium :: Maybe ImageUrl,
    _medium :: Maybe ImageUrl,
    _large :: Maybe ImageUrl,
    _original :: Maybe ImageUrl
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''ImageUrls

-----------------------------------------------------------------------------

-- | An object contains a single image url.
--
-- In 'Illust':
--   @
--     "meta_single_page": {
--       "original_image_url": "..."
--     }
--   @
newtype OriginalImageUrl = OriginalImageUrl
  { _originalImageUrl :: Maybe ImageUrl
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''OriginalImageUrl

-----------------------------------------------------------------------------

-- | A tag.
--
-- Example:
-- @
--   {
--     "name": "...",
--     "translated_name": null
--   }
-- @
data Tag = Tag
  { _name :: Text,
    _translatedName :: Maybe Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''Tag

-----------------------------------------------------------------------------

-- | Type of illustration.
--
-- In pixiv API, all of illustrations, mangas, and ugoiras are represented in 'Illust' data type.
-- So they can be distinguished by 'IllustType'.
data IllustType = TypeIllust | TypeManga | TypeUgoira
  deriving stock (Eq, Ord, Enum, Show, Read)

deriveEnumJSON "Type" ''IllustType
deriveEnumToHttpApiData "Type" ''IllustType

-----------------------------------------------------------------------------

-- | Manga series.
data Series = Series
  { _seriesId :: Int,
    _title :: Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON "series" ''Series

-----------------------------------------------------------------------------

-- | A page of 'Illust' containing 'ImageUrls'.
newtype MetaPage = MetaPage
  { _imageUrls :: ImageUrls
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''MetaPage

-----------------------------------------------------------------------------

-- | User data type.
data User = User
  { _userId :: Int,
    _name :: Text,
    _account :: Text,
    _profileImageUrls :: ImageUrls,
    _comment :: Maybe Text,
    -- | For login account.
    _isFollowed :: Maybe Bool
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON "user" ''User

-----------------------------------------------------------------------------

-- | Illustraion data type.
--
-- See 'IllustType'.
data Illust = Illust
  { _illustId :: Int,
    _title :: Text,
    _illustType :: IllustType,
    _imageUrls :: ImageUrls,
    _caption :: Text,
    _restrict :: Int,
    _user :: User,
    _tags :: [Tag],
    _tools :: [Text],
    _createDate :: UTCTime,
    _pageCount :: Int,
    _width :: Int,
    _height :: Int,
    _sanityLevel :: Int,
    _xRestrict :: Int,
    -- | Only manga may have this field.
    _series :: Maybe Series,
    -- | Only single page illustration has this field.
    _metaSinglePage :: Maybe OriginalImageUrl,
    -- | Only multi page illustration has this field.
    _metaPages :: [MetaPage],
    _totalView :: Int,
    _totalBookmarks :: Int,
    -- | For login account.
    _isBookmarked :: Bool,
    _visible :: Bool,
    _isMuted :: Bool,
    _totalComments :: Maybe Int
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON "illust" ''Illust

-----------------------------------------------------------------------------

-- | A trending tag.
--
-- Don't confuse with 'Tag'. 'TrendingTag' contains 'Illust',
-- and the textual name of the tag is called @tag@, instead of @name@ in 'Tag'.
data TrendingTag = TrendingTag
  { -- This is ugly, not consistent with normal 'Tag'
    _trendTag :: Text,
    _translatedName :: Maybe Text,
    _illust :: Illust
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON "trend" ''TrendingTag

-----------------------------------------------------------------------------

-- | A wrapper of 'TrendingTag's for JSON deserialization.
newtype TrendingTags = TrendingTags
  { _trend_tags :: [TrendingTag]
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''TrendingTags

-----------------------------------------------------------------------------

-- | Response of API which returns illustrations.
data Illusts = Illusts
  { _illusts :: [Illust],
    _nextUrl :: Maybe Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''Illusts

type instance NextUrlLess Illusts = [Illust]

instance HasNextUrl Illusts where
  unNextUrl Illusts {..} = _illusts
  getNextUrl Illusts {..} = _nextUrl

-----------------------------------------------------------------------------

-- | A wrapper of 'Illust' for JSON deserialization.
newtype IllustWrapper = IllustWrapper
  { _illust :: Illust
  }
  deriving stock (Show, Eq, Read)

derivePixivJSON' ''IllustWrapper

-----------------------------------------------------------------------------

-- | UserProfile data type.
--
-- Not sure if all fields are covered, and maybe some fields should be optional.
data UserProfile = UserProfile
  { _webpage :: Maybe Text,
    _gender :: Text,
    _birth :: Text,
    _birthDay :: Text,
    _birthYear :: Int,
    _region :: Text,
    _addressId :: Int,
    _countryCode :: Text,
    _job :: Text,
    _jobId :: Int,
    _totalFollowUsers :: Int,
    _totalMypixivUsers :: Int,
    _totalIllusts :: Int,
    _totalManga :: Int,
    _totalIllustBookmarksPublic :: Int,
    _totalIllustSeries :: Int,
    _totalNovelSeries :: Int,
    _backgroundImageUrl :: Maybe ImageUrl,
    _twitterAccount :: Text,
    _twitterUrl :: Maybe Text,
    _pawooUrl :: Maybe Text,
    _isPreminum :: Maybe Bool,
    _isUsingCustomProfileImage :: Bool
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''UserProfile

-----------------------------------------------------------------------------

-- | Publicity data type.
--
-- The value @public@ or @private@ are present in 'ProfilePublicity'.
-- This type is also used in @restrict@ query param.
data Publicity
  = Public
  | Private
  | -- | May not be available in @restrict@ query param
    Mypixiv
  deriving stock (Eq, Ord, Enum, Show, Read)

deriveEnumJSON' ''Publicity
deriveEnumToHttpApiData' ''Publicity

-----------------------------------------------------------------------------

-- | Profile publicity of a user.
--
-- Not sure if all fields are covered, and maybe some fields should be optional.
data ProfilePublicity = ProfilePublicity
  { _gender :: Publicity,
    _region :: Publicity,
    _birthDay :: Publicity,
    _birthYear :: Publicity,
    _job :: Publicity,
    _pawoo :: Bool
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''ProfilePublicity

-----------------------------------------------------------------------------

-- | Workspace information of a user.
-- Not sure if all fields are covered, and maybe some fields should be optional.
data Workspace = Workspace
  { _pc :: Text,
    _monitor :: Text,
    _tool :: Text,
    _scanner :: Text,
    _tablet :: Text,
    _mouse :: Text,
    _printer :: Text,
    _desktop :: Text,
    _music :: Text,
    _desk :: Text,
    _chair :: Text,
    _comment :: Text,
    _workspaceImageUrl :: Maybe Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''Workspace

-----------------------------------------------------------------------------

-- | Details of a user.
data UserDetail = UserDetail
  { _user :: User,
    _profile :: UserProfile,
    _profilePublicity :: ProfilePublicity,
    _workspace :: Workspace
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''UserDetail

-----------------------------------------------------------------------------

-- | A preview of user information.
--
-- Except 'Web.Pixiv.API.getUserDetail', other API involving users return this data type.
data UserPreview = UserPreview
  { _user :: User,
    _illusts :: [Illust],
    -- | Novels are not supported currently.
    _novels :: A.Value,
    _isMuted :: Bool
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''UserPreview

-----------------------------------------------------------------------------

-- | Response of API which returns user previews.
data UserPreviews = UserPreviews
  { _userPreviews :: [UserPreview],
    _nextUrl :: Maybe Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''UserPreviews

type instance NextUrlLess UserPreviews = [UserPreview]

instance HasNextUrl UserPreviews where
  unNextUrl UserPreviews {..} = _userPreviews
  getNextUrl UserPreviews {..} = _nextUrl

-----------------------------------------------------------------------------

-- | A comment.
data Comment = Comment
  { _commentId :: Int,
    _comment :: Text,
    _date :: UTCTime,
    _user :: User,
    -- TODO
    _parentComment :: Maybe A.Value
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON "comment" ''Comment

-----------------------------------------------------------------------------

-- | Response of API which returns comments.
data Comments = Comments
  { _totalComments :: Int,
    _comments :: [Comment],
    _nextUrl :: Maybe Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''Comments

type instance NextUrlLess Comments = [Comment]

instance HasNextUrl Comments where
  unNextUrl Comments {..} = _comments
  getNextUrl Comments {..} = _nextUrl

-----------------------------------------------------------------------------

-- | A ugoira frame.
data UgoiraFrame = UgoiraFrame
  { -- | File name.
    _ugoiraFile :: Text,
    -- | Duration of this frame (in millisecond).
    _ugoiraDelay :: Int
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON "ugoira" ''UgoiraFrame

-----------------------------------------------------------------------------

-- | A wrapper of ugoira zip file url.
newtype ZipUrls = ZipUrls
  { _zipMedium :: Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON "zip" ''ZipUrls

-----------------------------------------------------------------------------

-- | Ugoira is a frame animation, whose common information is represented in 'Illust'.
-- This metadata contains a link to download the zip archive, which compresses frames of the ugoira;
-- and 'UgoiraFrame's to represents metadata of each frame.
-- Using 'Web.Pixiv.API.getUgoiraMetadata' can obtain value of this type.
-- See 'Web.Pixiv.Utils.ugoiraMetadataToFFConcat' and 'Web.Pixiv.Download.downloadUgoiraToMP4'.
data UgoiraMetadata = UgoiraMetadata
  { _zipUrls :: ZipUrls,
    _frames :: [UgoiraFrame]
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''UgoiraMetadata

-----------------------------------------------------------------------------

-- | A wrapper of 'UgoiraMetadata' for JSON deserialization.
newtype UgoiraMetadataWrapper = UgoiraMetadataWrapper
  { _ugoiraMetadata :: UgoiraMetadata
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''UgoiraMetadataWrapper

-----------------------------------------------------------------------------

-- | Spotlight article.
data SpotlightArticle = SpotlightArticle
  { _saId :: Int,
    _title :: Text,
    _pureTitle :: Text,
    _thumbnail :: Text,
    _articleUrl :: Text,
    _publishDate :: UTCTime,
    _category :: Text,
    _subcategoryLabel :: Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''SpotlightArticle

-----------------------------------------------------------------------------

-- | Response of API which returns spotlight articles.
data SpotlightArticles = SpotlightArticles
  { _spotlightArticles :: [SpotlightArticle],
    _nextUrl :: Maybe Text
  }
  deriving stock (Eq, Show, Read)

derivePixivJSON' ''SpotlightArticles

type instance NextUrlLess SpotlightArticles = [SpotlightArticle]

instance HasNextUrl SpotlightArticles where
  unNextUrl SpotlightArticles {..} = _spotlightArticles
  getNextUrl SpotlightArticles {..} = _nextUrl

-----------------------------------------------------------------------------

-- | Rank mode query parm.
--
-- See 'Web.Pixiv.API.getIllustRanking'
data RankMode
  = Day
  | DayR18
  | DayMale
  | DayMaleR18
  | DayFemale
  | DayFemaleR18
  | Week
  | WeekR18
  | WeekR18G
  | Month
  | WeekOriginal
  | WeekRookie
  | DayManga
  deriving stock (Show, Eq, Ord, Enum, Read)

deriveEnumToHttpApiData' ''RankMode

-----------------------------------------------------------------------------

-- | Sorting method query parm.
--
-- See 'Web.Pixiv.API.searchIllust'.
data SortingMethod
  = DateDesc
  | DateAsc
  | PopularDesc
  deriving stock (Show, Eq, Ord, Enum, Read)

deriveEnumToHttpApiData' ''SortingMethod

-----------------------------------------------------------------------------

-- | Duration query parm.
--
-- See 'Web.Pixiv.API.searchIllust'.
data Duration
  = WithinLastDay
  | WithinLastMonth
  | WithinLastYear
  deriving stock (Show, Eq, Ord, Read)

deriveEnumToHttpApiData' ''Duration

-----------------------------------------------------------------------------

-- | Search target query parm.
--
-- See 'Web.Pixiv.API.searchIllust'.
data SearchTarget = ExactMatchForTags | PartialMatchForTags | TitleAndCaption
  deriving stock (Show, Eq, Ord, Enum, Read)

deriveEnumToHttpApiData' ''SearchTarget

-----------------------------------------------------------------------------