jmapi

package module
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 28 Imported by: 0

README

jmapi (Go)

Go 模块名:github.com/laoin114514/jmapi

这是从 JMComic-Crawler-Python 的 API 能力迁移出的 Go 版本(不含命令行)。

安装

go get github.com/laoin114514/jmapi

快速开始

package main

import (
	"fmt"
	"github.com/laoin114514/jmapi"
)

func main() {
	client := jmapi.NewClient(jmapi.Config{
		ClientType: jmapi.ClientTypeAPI,
	})

	album, err := client.GetAlbumDetail("123456")
	if err != nil {
		panic(err)
	}
	fmt.Println(album.ID, album.Name)
}

所有公开接口与示例

构造与配置
NewClient(cfg Config) *Client
client := jmapi.NewClient(jmapi.Config{
	ClientType: jmapi.ClientTypeAPI,
	Domains:    []string{"www.cdnaspa.vip"},
	RetryTimes: 3,
})
SetDomains(domains []string) / Domains() []string
client.SetDomains([]string{"www.cdnaspa.club", "www.cdnplaystation6.vip"})
fmt.Println(client.Domains())
UpdateCookies(cookies map[string]string)
client.UpdateCookies(map[string]string{
	"AVS": "your_avs_cookie",
})

详情接口
GetAlbumDetail(albumID string) (*AlbumDetail, error)
album, err := client.GetAlbumDetail("123456")
if err != nil {
	panic(err)
}
fmt.Println(album.Name, album.Tags)
GetPhotoDetail(photoID string, fetchAlbum bool, fetchScrambleID bool) (*PhotoDetail, error)
photo, err := client.GetPhotoDetail("654321", true, true)
if err != nil {
	panic(err)
}
fmt.Println(photo.ID, photo.AlbumID, photo.ScrambleID)
GetScrambleID(photoID string) (string, error)
sid, err := client.GetScrambleID("654321")
if err != nil {
	panic(err)
}
fmt.Println("scramble id:", sid)

搜索接口
Search(searchQuery string, page int, mainTag int, orderBy, timeRange, category, subCategory string) (*SearchResult, error)
res, err := client.Search("MANA", 1, 0, "mr", "a", "0", "")
if err != nil {
	panic(err)
}
fmt.Println(res.Total, len(res.Items))
SearchSite(searchQuery string, page int, orderBy, timeRange string)
res, _ := client.SearchSite("人妻", 1, "mr", "a")
fmt.Println(len(res.Items))
SearchAuthor(searchQuery string, page int, orderBy, timeRange string)
res, _ := client.SearchAuthor("作者名", 1, "mr", "a")
fmt.Println(len(res.Items))
SearchTag(searchQuery string, page int, orderBy, timeRange string)
res, _ := client.SearchTag("無修正", 1, "mr", "a")
fmt.Println(len(res.Items))
SearchActor(searchQuery string, page int, orderBy, timeRange string)
res, _ := client.SearchActor("角色名", 1, "mr", "a")
fmt.Println(len(res.Items))

分类/排行接口
CategoriesFilter(page int, timeRange, category, orderBy, subCategory string) (*SearchResult, error)
res, err := client.CategoriesFilter(1, "a", "0", "mv", "")
if err != nil {
	panic(err)
}
fmt.Println(len(res.Items))
MonthRanking(page int, category string)
res, _ := client.MonthRanking(1, "0")
fmt.Println(len(res.Items))
WeekRanking(page int, category string)
res, _ := client.WeekRanking(1, "0")
fmt.Println(len(res.Items))
DayRanking(page int, category string)
res, _ := client.DayRanking(1, "0")
fmt.Println(len(res.Items))

用户接口
Login(username, password string) (map[string]any, error)
profile, err := client.Login("your_username", "your_password")
if err != nil {
	panic(err)
}
fmt.Println(profile["username"], profile["uid"])
FavoriteFolder(page int, orderBy, folderID string) (*FavoriteResult, error)
fav, err := client.FavoriteFolder(1, "mr", "0")
if err != nil {
	panic(err)
}
fmt.Println(fav.Total, len(fav.Items))
AddFavoriteAlbum(albumID, folderID string) (map[string]any, error)
ret, err := client.AddFavoriteAlbum("123456", "0")
if err != nil {
	panic(err)
}
fmt.Println(ret)
AlbumComment(videoID, comment, originator, status, commentID string) (map[string]any, error)
ret, err := client.AlbumComment("123456", "测试评论", "", "true", "")
if err != nil {
	panic(err)
}
fmt.Println(ret)

回复评论示例:

ret, err := client.AlbumComment("123456", "回复内容", "", "", "999999")
if err != nil {
	panic(err)
}
fmt.Println(ret)

图片接口
DownloadImage(imgURL string) ([]byte, error)
b, err := client.DownloadImage("https://cdn-msp.jmapiproxy1.cc/media/photos/123456/00001.jpg")
if err != nil {
	panic(err)
}
fmt.Println("bytes:", len(b))
DownloadAlbumCover(albumID string) ([]byte, error)
b, err := client.DownloadAlbumCover("123456")
if err != nil {
	panic(err)
}
fmt.Println("cover bytes:", len(b))

与 Python 版本关系说明

  • 本 Go 版本聚焦“API 提供器”能力,不包含命令行。
  • 已覆盖核心:详情、搜索、分类/排行、登录/收藏/评论、图片下载。
  • Python 项目中的高级特性(插件系统、复杂下载调度、HTML 解析端完整兼容、图片分片重组解码链路)暂未全部迁移。

Documentation

Index

Constants

View Source
const (
	DefaultAppVersion      = "2.0.19"
	AppTokenSecret         = "18comicAPP"
	AppTokenSecret2        = "18comicAPPContent"
	AppDataSecret          = "185Hcomic3PAPP7R"
	APIDomainServerSecret  = "diosfjckwpqpdfjkvnqQjsik"
	DefaultHTMLUserAgent   = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
	DefaultMobileUserAgent = "" /* 156-byte string literal not displayed */
)
View Source
const (
	OrderByLatest  = "mr"
	OrderByView    = "mv"
	OrderByPicture = "mp"
	OrderByLike    = "tf"
	OrderByScore   = "tr"
	OrderByComment = "md"
)

排序

View Source
const (
	TimeToday = "t"
	TimeWeek  = "w"
	TimeMonth = "m"
	TimeAll   = "a"
)

时间范围

View Source
const (
	CategoryAll         = "0"
	CategoryDoujin      = "doujin"
	CategorySingle      = "single"
	CategoryShort       = "short"
	CategoryAnother     = "another"
	CategoryHanman      = "hanman"
	CategoryMeiman      = "meiman"
	CategoryDoujinCos   = "doujin_cosplay"
	Category3D          = "3D"
	CategoryEnglishSite = "english_site"
)

分类

Variables

View Source
var DefaultAPIDomainServerURLs = []string{
	"https://rup4a04-c01.tos-ap-southeast-1.bytepluses.com/newsvr-2025.txt",
	"https://rup4a04-c02.tos-cn-hongkong.bytepluses.com/newsvr-2025.txt",
}
View Source
var DefaultAPIDomains = []string{
	"www.cdnaspa.vip",
	"www.cdnaspa.club",
	"www.cdnplaystation6.vip",
	"www.cdnplaystation6.cc",
}
View Source
var DefaultHTMLDomains = []string{
	"18comic.vip",
}

Functions

func RegisterPluginFactory added in v0.1.3

func RegisterPluginFactory(key string, factory PluginFactory)

Types

type APIResponse

type APIResponse struct {
	Code     int            `json:"code"`
	ErrorMsg string         `json:"errorMsg"`
	Data     map[string]any `json:"data"`
}

type AlbumBatchResult added in v0.1.2

type AlbumBatchResult struct {
	ID    string
	Album *AlbumDetail
	Err   error
}

func DownloadAlbumsBatch added in v0.1.2

func DownloadAlbumsBatch(option Option, albumIDs []string, workers int) []AlbumBatchResult

DownloadAlbumsBatch 并发下载多个 album(走 Downloader 框架)。

func FetchAlbumDetailsBatch added in v0.1.2

func FetchAlbumDetailsBatch(client *Client, albumIDs []string, workers int) []AlbumBatchResult

FetchAlbumDetailsBatch 并发获取多个 album 详情(不下载图片)。

type AlbumDetail

type AlbumDetail struct {
	ID           string         `json:"id"`
	Name         string         `json:"name"`
	Author       []string       `json:"author,omitempty"`
	Description  string         `json:"description,omitempty"`
	Tags         []string       `json:"tags,omitempty"`
	Works        []string       `json:"works,omitempty"`
	Actors       []string       `json:"actors,omitempty"`
	PageCount    int            `json:"page_count,omitempty"`
	CommentCount int            `json:"comment_count,omitempty"`
	Likes        string         `json:"likes,omitempty"`
	Views        string         `json:"views,omitempty"`
	EpisodeIDs   []string       `json:"episode_ids,omitempty"`
	Raw          map[string]any `json:"raw,omitempty"`
}

type AlbumListItem

type AlbumListItem struct {
	ID       string   `json:"id"`
	Name     string   `json:"name"`
	Label    string   `json:"label,omitempty"`
	Category string   `json:"category,omitempty"`
	TagList  []string `json:"tag_list,omitempty"`
}

type CategoryParams added in v0.1.2

type CategoryParams struct {
	Page        int
	TimeRange   string
	Category    string
	OrderBy     string
	SubCategory string
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(cfg Config) *Client

func (*Client) AddFavoriteAlbum

func (c *Client) AddFavoriteAlbum(albumID, folderID string) (map[string]any, error)

func (*Client) AlbumComment

func (c *Client) AlbumComment(videoID, comment, originator, status, commentID string) (map[string]any, error)

func (*Client) AutoUpdateDomains

func (c *Client) AutoUpdateDomains() error

func (*Client) CategoriesFilter

func (c *Client) CategoriesFilter(page int, timeRange, category, orderBy, subCategory string) (*SearchResult, error)

func (*Client) CategoriesPages added in v0.1.2

func (c *Client) CategoriesPages(params CategoryParams, handler func(page int, result *SearchResult) (bool, error)) error

CategoriesPages 按页遍历分类结果。

func (*Client) CheckPhoto

func (c *Client) CheckPhoto(photo *PhotoDetail) error

func (*Client) DayRanking

func (c *Client) DayRanking(page int, category string) (*SearchResult, error)

func (*Client) Domains

func (c *Client) Domains() []string

func (*Client) DownloadAlbumCover

func (c *Client) DownloadAlbumCover(albumID string) ([]byte, error)

func (*Client) DownloadByImageDetail

func (c *Client) DownloadByImageDetail(photoID, imageName string) ([]byte, error)

func (*Client) DownloadImage

func (c *Client) DownloadImage(imgURL string) ([]byte, error)

func (*Client) FavoriteFolder

func (c *Client) FavoriteFolder(page int, orderBy, folderID, username string) (*FavoriteResult, error)

func (*Client) FavoritePages added in v0.1.2

func (c *Client) FavoritePages(params FavoriteParams, handler func(page int, result *FavoriteResult) (bool, error)) error

FavoritePages 按页遍历收藏夹。

func (*Client) GetAlbumDetail

func (c *Client) GetAlbumDetail(albumID string) (*AlbumDetail, error)

func (*Client) GetPhotoDetail

func (c *Client) GetPhotoDetail(photoID string, fetchAlbum bool, fetchScrambleID bool) (*PhotoDetail, error)

func (*Client) GetScrambleID

func (c *Client) GetScrambleID(photoID string) (string, error)

func (*Client) Login

func (c *Client) Login(username, password string) (map[string]any, error)

func (*Client) MonthRanking

func (c *Client) MonthRanking(page int, category string) (*SearchResult, error)

func (*Client) Search

func (c *Client) Search(searchQuery string, page int, mainTag int, orderBy, timeRange, category, subCategory string) (*SearchResult, error)

func (*Client) SearchActor

func (c *Client) SearchActor(searchQuery string, page int, orderBy, timeRange, category, subCategory string) (*SearchResult, error)

func (*Client) SearchAuthor

func (c *Client) SearchAuthor(searchQuery string, page int, orderBy, timeRange, category, subCategory string) (*SearchResult, error)

func (*Client) SearchPages added in v0.1.2

func (c *Client) SearchPages(params SearchParams, handler func(page int, result *SearchResult) (bool, error)) error

SearchPages 按页遍历搜索结果。handler 返回 false 可提前终止。

func (*Client) SearchSite

func (c *Client) SearchSite(searchQuery string, page int, orderBy, timeRange, category, subCategory string) (*SearchResult, error)

func (*Client) SearchTag

func (c *Client) SearchTag(searchQuery string, page int, orderBy, timeRange, category, subCategory string) (*SearchResult, error)

func (*Client) SearchWork

func (c *Client) SearchWork(searchQuery string, page int, orderBy, timeRange, category, subCategory string) (*SearchResult, error)

func (*Client) SetDomains

func (c *Client) SetDomains(domains []string)

func (*Client) Setting

func (c *Client) Setting() (map[string]any, error)

func (*Client) UpdateCookies

func (c *Client) UpdateCookies(cookies map[string]string)

func (*Client) WeekRanking

func (c *Client) WeekRanking(page int, category string) (*SearchResult, error)

type ClientType

type ClientType string
const (
	ClientTypeAPI  ClientType = "api"
	ClientTypeHTML ClientType = "html"
)

type Config

type Config struct {
	ClientType        ClientType
	Domains           []string
	Timeout           time.Duration
	RetryTimes        int
	AppVersion        string
	Proxies           map[string]string
	Cookies           map[string]string
	Headers           map[string]string
	AutoUpdateHost    bool
	AutoEnsureCookies bool
	UseFixedTimestamp bool
}

type DirRule

type DirRule struct {
	Rule        string
	BaseDir     string
	NormalizeZH string
}

type DownloadOptions

type DownloadOptions struct {
	Image     ImageOptions
	Threading ThreadingOptions
}

type Downloader

type Downloader struct {
	Option  Option
	Client  *Client
	Plugins *PluginManager

	SuccessImages map[string][]string // photoID -> saved paths
	FailedImages  []ImageFailure
	FailedPhotos  []PhotoFailure
	// contains filtered or unexported fields
}

func NewDownloader

func NewDownloader(option Option) *Downloader

func (*Downloader) DownloadAlbum

func (d *Downloader) DownloadAlbum(albumID string) (*AlbumDetail, error)

func (*Downloader) DownloadPhoto

func (d *Downloader) DownloadPhoto(photoID string) (*PhotoDetail, error)

func (*Downloader) HasFailures

func (d *Downloader) HasFailures() bool

func (*Downloader) RaiseIfHasFailures

func (d *Downloader) RaiseIfHasFailures() error

func (*Downloader) RegisterPlugin

func (d *Downloader) RegisterPlugin(plugin Plugin)

type FavoriteParams added in v0.1.2

type FavoriteParams struct {
	Page     int
	OrderBy  string
	FolderID string
	Username string
}

type FavoriteResult

type FavoriteResult struct {
	Total int             `json:"total"`
	Items []AlbumListItem `json:"items"`
	Raw   map[string]any  `json:"raw,omitempty"`
}

type ImageFailure

type ImageFailure struct {
	PhotoID  string
	ImageURL string
	SavePath string
	Err      error
}

type ImageOptions

type ImageOptions struct {
	Decode bool
	Suffix string
}

type ImageSuffixFilterPlugin added in v0.1.3

type ImageSuffixFilterPlugin struct {
	PluginAdapter
	// contains filtered or unexported fields
}

func (*ImageSuffixFilterPlugin) BeforeImage added in v0.1.3

func (p *ImageSuffixFilterPlugin) BeforeImage(ctx PluginContext, photo *PhotoDetail, imageURL, savePath string) error

func (*ImageSuffixFilterPlugin) Configure added in v0.1.3

func (p *ImageSuffixFilterPlugin) Configure(kwargs map[string]any) error

func (*ImageSuffixFilterPlugin) Key added in v0.1.3

type Option

type Option struct {
	Log          bool
	Version      string
	ClientConfig Config
	DirRule      DirRule
	Download     DownloadOptions
	Plugins      PluginGroup
	UseCache     bool
}

func DefaultOption

func DefaultOption() Option

func LoadOption

func LoadOption(path string) (Option, error)

func LoadOptionFromYAMLText added in v0.1.1

func LoadOptionFromYAMLText(text string) (Option, error)

func (Option) DecideImageFilename

func (o Option) DecideImageFilename(index int) string

func (Option) DecideImageSaveDir

func (o Option) DecideImageSaveDir(album AlbumDetail, photo PhotoDetail) (string, error)

func (Option) DecideImageSuffix

func (o Option) DecideImageSuffix(original string) string

func (Option) NewClient

func (o Option) NewClient() *Client

type PhotoBatchResult added in v0.1.2

type PhotoBatchResult struct {
	ID    string
	Photo *PhotoDetail
	Err   error
}

func FetchPhotoDetailsBatch added in v0.1.2

func FetchPhotoDetailsBatch(client *Client, photoIDs []string, workers int, fetchAlbum, fetchScrambleID bool) []PhotoBatchResult

FetchPhotoDetailsBatch 并发获取多个 photo 详情。

type PhotoDetail

type PhotoDetail struct {
	ID         string         `json:"id"`
	AlbumID    string         `json:"album_id,omitempty"`
	Name       string         `json:"name"`
	SeriesID   string         `json:"series_id,omitempty"`
	Sort       string         `json:"sort,omitempty"`
	ScrambleID string         `json:"scramble_id,omitempty"`
	PageArr    []string       `json:"page_arr,omitempty"`
	Raw        map[string]any `json:"raw,omitempty"`
}

type PhotoFailure

type PhotoFailure struct {
	PhotoID string
	Err     error
}

type Plugin

type Plugin interface {
	Key() string
	Configure(kwargs map[string]any) error
	AfterInit(ctx PluginContext) error
	BeforeAlbum(ctx PluginContext, album *AlbumDetail) error
	AfterAlbum(ctx PluginContext, album *AlbumDetail) error
	BeforePhoto(ctx PluginContext, photo *PhotoDetail) error
	AfterPhoto(ctx PluginContext, photo *PhotoDetail) error
	BeforeImage(ctx PluginContext, photo *PhotoDetail, imageURL string, savePath string) error
	AfterImage(ctx PluginContext, photo *PhotoDetail, imageURL string, savePath string) error
}

func BuildPluginFromConfig added in v0.1.3

func BuildPluginFromConfig(cfg PluginConfig) (Plugin, error)

type PluginAdapter

type PluginAdapter struct{}

func (PluginAdapter) AfterAlbum

func (PluginAdapter) AfterAlbum(ctx PluginContext, album *AlbumDetail) error

func (PluginAdapter) AfterImage

func (PluginAdapter) AfterImage(ctx PluginContext, photo *PhotoDetail, imageURL string, savePath string) error

func (PluginAdapter) AfterInit

func (PluginAdapter) AfterInit(ctx PluginContext) error

func (PluginAdapter) AfterPhoto

func (PluginAdapter) AfterPhoto(ctx PluginContext, photo *PhotoDetail) error

func (PluginAdapter) BeforeAlbum

func (PluginAdapter) BeforeAlbum(ctx PluginContext, album *AlbumDetail) error

func (PluginAdapter) BeforeImage

func (PluginAdapter) BeforeImage(ctx PluginContext, photo *PhotoDetail, imageURL string, savePath string) error

func (PluginAdapter) BeforePhoto

func (PluginAdapter) BeforePhoto(ctx PluginContext, photo *PhotoDetail) error

func (PluginAdapter) Configure added in v0.1.3

func (PluginAdapter) Configure(kwargs map[string]any) error

func (PluginAdapter) Key

func (PluginAdapter) Key() string

type PluginConfig

type PluginConfig struct {
	Plugin string
	Kwargs map[string]any
	Safe   bool
	Log    bool
	Valid  string
}

type PluginContext

type PluginContext struct {
	Option     *Option
	Downloader *Downloader
	Client     *Client
}

type PluginFactory added in v0.1.3

type PluginFactory func() Plugin

type PluginGroup

type PluginGroup struct {
	Valid       string
	BeforeAlbum []PluginConfig
	AfterAlbum  []PluginConfig
	BeforePhoto []PluginConfig
	AfterPhoto  []PluginConfig
	BeforeImage []PluginConfig
	AfterImage  []PluginConfig
	AfterInit   []PluginConfig
}

type PluginManager

type PluginManager struct {
	// contains filtered or unexported fields
}

func NewPluginManager

func NewPluginManager() *PluginManager

func (*PluginManager) AfterAlbum

func (pm *PluginManager) AfterAlbum(ctx PluginContext, album *AlbumDetail) error

func (*PluginManager) AfterImage

func (pm *PluginManager) AfterImage(ctx PluginContext, photo *PhotoDetail, imageURL, savePath string) error

func (*PluginManager) AfterInit

func (pm *PluginManager) AfterInit(ctx PluginContext) error

func (*PluginManager) AfterPhoto

func (pm *PluginManager) AfterPhoto(ctx PluginContext, photo *PhotoDetail) error

func (*PluginManager) BeforeAlbum

func (pm *PluginManager) BeforeAlbum(ctx PluginContext, album *AlbumDetail) error

func (*PluginManager) BeforeImage

func (pm *PluginManager) BeforeImage(ctx PluginContext, photo *PhotoDetail, imageURL, savePath string) error

func (*PluginManager) BeforePhoto

func (pm *PluginManager) BeforePhoto(ctx PluginContext, photo *PhotoDetail) error

func (*PluginManager) Register

func (pm *PluginManager) Register(p Plugin)

func (*PluginManager) RegisterWithPolicy added in v0.1.3

func (pm *PluginManager) RegisterWithPolicy(p Plugin, safe bool, logEnable bool, valid string)

type RetryTuningPlugin added in v0.1.3

type RetryTuningPlugin struct {
	PluginAdapter
	// contains filtered or unexported fields
}

func (*RetryTuningPlugin) AfterInit added in v0.1.3

func (p *RetryTuningPlugin) AfterInit(ctx PluginContext) error

func (*RetryTuningPlugin) Configure added in v0.1.3

func (p *RetryTuningPlugin) Configure(kwargs map[string]any) error

func (*RetryTuningPlugin) Key added in v0.1.3

func (p *RetryTuningPlugin) Key() string

type SearchParams added in v0.1.2

type SearchParams struct {
	Query       string
	MainTag     int
	Page        int
	OrderBy     string
	TimeRange   string
	Category    string
	SubCategory string
}

type SearchResult

type SearchResult struct {
	Total int             `json:"total"`
	Items []AlbumListItem `json:"items"`
	Raw   map[string]any  `json:"raw,omitempty"`
}

type ThreadingOptions

type ThreadingOptions struct {
	Image int
	Photo int
}

type TopicFilterPlugin added in v0.1.3

type TopicFilterPlugin struct {
	PluginAdapter
	// contains filtered or unexported fields
}

func (*TopicFilterPlugin) AfterAlbum added in v0.1.3

func (p *TopicFilterPlugin) AfterAlbum(ctx PluginContext, album *AlbumDetail) error

func (*TopicFilterPlugin) AfterImage added in v0.1.3

func (p *TopicFilterPlugin) AfterImage(ctx PluginContext, photo *PhotoDetail, imageURL, savePath string) error

func (*TopicFilterPlugin) AfterPhoto added in v0.1.3

func (p *TopicFilterPlugin) AfterPhoto(ctx PluginContext, photo *PhotoDetail) error

func (*TopicFilterPlugin) BeforeAlbum added in v0.1.3

func (p *TopicFilterPlugin) BeforeAlbum(ctx PluginContext, album *AlbumDetail) error

func (*TopicFilterPlugin) BeforeImage added in v0.1.3

func (p *TopicFilterPlugin) BeforeImage(ctx PluginContext, photo *PhotoDetail, imageURL, savePath string) error

func (*TopicFilterPlugin) BeforePhoto added in v0.1.3

func (p *TopicFilterPlugin) BeforePhoto(ctx PluginContext, photo *PhotoDetail) error

func (*TopicFilterPlugin) Configure added in v0.1.3

func (p *TopicFilterPlugin) Configure(kwargs map[string]any) error

func (*TopicFilterPlugin) Key added in v0.1.3

func (p *TopicFilterPlugin) Key() string

Directories

Path Synopsis
example
album_detail command
comment command
favorite command
image command
option_usage command
photo_detail command
plugin_simple command
ranking command
search command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL