Merge pull request 'Novel comments and special marks' (#108) from jackyzy823/PixivFE:novel-page-improve into v2

Reviewed-on: https://codeberg.org/VnPower/PixivFE/pulls/108
This commit is contained in:
VnPower
2024-09-22 14:06:08 +00:00
8 changed files with 198 additions and 71 deletions
+34 -30
View File
@@ -832,36 +832,6 @@ edit style.scss
.illust .illust-description {
font-size: 0.85em;
}
.illust .comment {
display: flex;
margin-bottom: 10px;
}
.illust .comment p {
margin: 0;
font-size: 0.8em;
}
.illust .comment .comment-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.illust .comment .comment-author {
color: #fff;
font-size: medium;
font-weight: bold;
}
.illust .comment .stamp {
width: 96px;
height: 96px;
border-radius: 4px;
}
.illust .comment .emoji {
width: 24px;
height: 24px;
margin-left: 3px;
margin-right: 3px;
}
.illust .illust-other-works a.illust-other-works-author {
display: inline-flex;
align-items: center;
@@ -877,6 +847,36 @@ edit style.scss
object-fit: cover;
object-position: center top;
}
.comment {
display: flex;
margin-bottom: 10px;
}
.comment p {
margin: 0;
font-size: 0.8em;
}
.comment .comment-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.comment .comment-author {
color: #fff;
font-size: medium;
font-weight: bold;
}
.comment .stamp {
width: 96px;
height: 96px;
border-radius: 4px;
}
.comment .emoji {
width: 24px;
height: 24px;
margin-left: 3px;
margin-right: 3px;
}
.novel-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(407px, 1fr));
@@ -989,6 +989,10 @@ edit style.scss
max-height: 100%;
margin: 0 0.5rem;
}
.novel .novel-content .novel-content-text a {
text-decoration: underline;
color: black;
}
.novel .novel-attr {
display: flex;
align-items: center;
+41 -36
View File
@@ -1111,42 +1111,6 @@ $font-fallback-sans: YuGothic,
}
.comment {
display: flex;
margin-bottom: 10px;
p {
margin: 0;
font-size: 0.8em;
}
.comment-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.comment-author {
color: $fg;
font-size: medium;
font-weight: bold;
}
.stamp {
width: 96px;
height: 96px;
border-radius: 4px;
}
.emoji {
width: 24px;
height: 24px;
margin-left: 3px;
margin-right: 3px;
}
}
.illust-other-works {
a.illust-other-works-author {
display: inline-flex;
@@ -1167,6 +1131,42 @@ $font-fallback-sans: YuGothic,
}
}
.comment {
display: flex;
margin-bottom: 10px;
p {
margin: 0;
font-size: 0.8em;
}
.comment-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.comment-author {
color: $fg;
font-size: medium;
font-weight: bold;
}
.stamp {
width: 96px;
height: 96px;
border-radius: 4px;
}
.emoji {
width: 24px;
height: 24px;
margin-left: 3px;
margin-right: 3px;
}
}
.novel-container {
display: grid;
@@ -1297,6 +1297,11 @@ $font-fallback-sans: YuGothic,
max-height: 100%;
margin: 0 0.5rem;
}
a {
text-decoration: underline;
color: black;
}
}
}
+3 -3
View File
@@ -1,12 +1,12 @@
{{- if !.IsUgoira }}
<div class="illust-images">
{{- range index := .Images }}
<a href="{{ .Original }}" target="_blank">
<a href="{{ .Original }}" target="_blank" id="{{ index + 1 }}">
{{- if .Width != 0 && .Height != 0 }}
{* todo: do not reflow the page because of loading image *}
<img src="{{ .Large }}" alt="Page {{ index }}" data-width="{{.Width}}" data-height="{{.Height}}" />
<img src="{{ .Large }}" alt="Page {{ index + 1 }}" data-width="{{.Width}}" data-height="{{.Height}}" />
{{- else }}
<img src="{{ .Large }}" alt="Page {{ index }}" />
<img src="{{ .Large }}" alt="Page {{ index + 1 }}" />
{{- end }}
</a>
{{- end }}
+31
View File
@@ -122,6 +122,37 @@
</div>
</div>
</div>
<hr />
<details>
<summary>Comments</summary>
{{- if .Novel.CommentOff == 1 }}
<p>The creator turned comments off</p>
{{- else if .Novel.CommentCount == 0 }}
<p>There are no comments yet</p>
{{- else }} {{ range .Novel.CommentsList }}
<div class="comment">
<a href="/users/{{ .AuthorID }}" class="comment-author">
<img class="comment-avatar" src="{{ .Avatar }}" alt="{{ .AuthorName }}" />
</a>
<div class="comment-context">
<a href="/users/{{ .AuthorID }}" class="comment-author">
{{- .AuthorName }}
</a>
<p>
{{- if .Stamp }}
<img class="stamp" src="/proxy/s.pximg.net/common/images/stamp/generated-stamps/{{ .Stamp }}_s.jpg"
alt="/proxy/s.pximg.net/common/images/stamp/generated-stamps/{{ .Stamp }}_s.jpg" />
{{- else }} {{ raw: parseEmojis(.Context) }} {{ end }}
</p>
<small>{{ .Date }}</small>
</div>
</div>
{{- end }} {{ end }}
</details>
</div>
<div class="container">
<h2>Related works</h2>
<div class="novel-container">
{{- range .NovelRelated }}
{{- include "fragments/novel-tn" . }}
+6
View File
@@ -141,6 +141,12 @@ func GetNovelRelatedURL(id string, limit int) string {
return fmt.Sprintf(base, id, limit)
}
func GetNovelCommentsURL(id string) string {
base := "https://www.pixiv.net/ajax/novels/comments/roots?novel_id=%s&offset=0&limit=100"
return fmt.Sprintf(base, id)
}
func GetNovelSeriesURL(id string) string {
base := "https://www.pixiv.net/ajax/novel/series/%s"
+47 -1
View File
@@ -3,6 +3,7 @@ package core
import (
"fmt"
"regexp"
"strings"
"time"
"net/http"
@@ -60,6 +61,18 @@ type Novel struct {
ViewMode int `json:"viewMode"`
// ...
} `json:"suggestedSettings"`
TextEmbeddedImages map[string]struct {
NovelImageId string `json:"novelImageId"`
SanityLevel string `json:"sl"`
Urls struct {
Two40Mw string `json:"240mw"`
Four80Mw string `json:"480mw"`
One200X1200 string `json:"1200x1200"`
One28X128 string `json:"128x128"`
Original string `json:"original"`
} `json:"urls"`
} `json:"textEmbeddedImages"`
CommentsList []Comment
}
type NovelBrief struct {
@@ -123,7 +136,19 @@ func GetNovelByID(r *http.Request, id string) (Novel, error) {
url := re_t.FindString(response)
url = session.ProxyImageUrl(r, url[11:]) // truncate the "original":
return fmt.Sprintf(`<img src=%s alt="%s"/>`, url, s)
// make [pixivimage:illustid-index] jump to anchor
link := fmt.Sprintf("/artworks/%s", strings.ReplaceAll(illustid, "-", "#"))
return fmt.Sprintf(`<a href="%s" target="_blank"><img src=%s alt="%s"/></a>`, link, url, s)
})
re_u := regexp.MustCompile(`\[uploadedimage:(\d+)\]`)
re_id := regexp.MustCompile(`\d+`)
novel.Content = re_u.ReplaceAllStringFunc(novel.Content, func(s string) string {
imageId := re_id.FindString(s)
if val, ok := novel.TextEmbeddedImages[imageId]; ok {
return fmt.Sprintf(`<img src=%s alt="%s"/>`, val.Urls.Original, s)
}
return s
})
return novel, nil
@@ -150,3 +175,24 @@ func GetNovelRelated(r *http.Request, id string) ([]NovelBrief, error) {
return novels.List, nil
}
func GetNovelComments(r *http.Request, id string) ([]Comment, error) {
var body struct {
Comments []Comment `json:"comments"`
}
URL := GetNovelCommentsURL(id)
response, err := API_GET_UnwrapJson(r.Context(), URL, "")
if err != nil {
return nil, err
}
response = session.ProxyImageUrl(r, response)
err = json.Unmarshal([]byte(response), &body)
if err != nil {
return nil, err
}
return body.Comments, nil
}
+8
View File
@@ -26,6 +26,14 @@ func NovelPage(w http.ResponseWriter, r *http.Request) error {
return err
}
if novel.CommentOff == 0 {
// TODO should use token only if R-18/R-18G
comments, err := core.GetNovelComments(r, id)
if err == nil {
novel.CommentsList = comments
}
}
user, err := core.GetUserBasicInformation(r, novel.UserID)
if err != nil {
return err
+28 -1
View File
@@ -305,8 +305,35 @@ func GetTemplateFunctions() map[string]any {
return s[:len(s)-6]
},
"renderNovel": func(s string) HTML {
furiganaTemplate := `<ruby>$1<rp>(</rp><rt>$2</rt><rp>)</rp></ruby>`
furiganaPattern := regexp.MustCompile(`\[\[rb:\s*(.+?)\s*>\s*(.+?)\s*\]\]`)
s = furiganaPattern.ReplaceAllString(s, furiganaTemplate)
chapterTemplate := `<h2>$1</h2>`
chapterPattern := regexp.MustCompile(`\[chapter:\s*(.+?)\s*\]`)
s = chapterPattern.ReplaceAllString(s, chapterTemplate)
jumpUriTemplate := `<a href="$2" target="_blank">$1</a>`
jumpUriPattern := regexp.MustCompile(`\[\[jumpuri:\s*(.+?)\s*>\s*(.+?)\s*\]\]`)
s = jumpUriPattern.ReplaceAllString(s, jumpUriTemplate)
jumpPageTemplate := `<a href="#$1">To page $1</a>`
jumpPagePattern := regexp.MustCompile(`\[jump:\s*(\d+?)\s*\]`)
s = jumpPagePattern.ReplaceAllString(s, jumpPageTemplate)
if strings.Contains(s, "[newpage]") {
// if [newpage] in content , then prepend <hr id="1"/> to the page
s = `<hr id="1"/>` + s
pageIdx := 1
// Should run before replace `\n` -> `<br />`
newPagePattern := regexp.MustCompile(`\s*\[newpage\]\s*`)
s = newPagePattern.ReplaceAllStringFunc(s, func(_ string) string {
pageIdx += 1
return fmt.Sprintf(`<br /><hr id="%d"/>`, pageIdx)
})
}
s = strings.ReplaceAll(s, "\n", "<br />")
s = strings.ReplaceAll(s, "[newpage]", "Insert page separator here.")
return HTML(s)
},
"novelGenre": GetNovelGenre,