UP 视频模块
parent
7964879495
commit
47f42fe2b0
|
@ -0,0 +1,7 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func InitApi() {
|
||||||
|
http.HandleFunc("/api/bilibili", GetVideoData)
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VideoData struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Ttl int `json:"ttl"`
|
||||||
|
Data struct {
|
||||||
|
Bvid string `json:"bvid"`
|
||||||
|
Aid int `json:"aid"`
|
||||||
|
Videos int `json:"videos"`
|
||||||
|
Tid int `json:"tid"`
|
||||||
|
Tname string `json:"tname"`
|
||||||
|
Copyright int `json:"copyright"`
|
||||||
|
Pic string `json:"pic"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Pubdate int `json:"pubdate"`
|
||||||
|
Ctime int `json:"ctime"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
|
DescV2 []struct {
|
||||||
|
RawText string `json:"raw_text"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
BizId int `json:"biz_id"`
|
||||||
|
} `json:"desc_v2"`
|
||||||
|
State int `json:"state"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
MissionId int `json:"mission_id"`
|
||||||
|
Rights struct {
|
||||||
|
Bp int `json:"bp"`
|
||||||
|
Elec int `json:"elec"`
|
||||||
|
Download int `json:"download"`
|
||||||
|
Movie int `json:"movie"`
|
||||||
|
Pay int `json:"pay"`
|
||||||
|
Hd5 int `json:"hd5"`
|
||||||
|
NoReprint int `json:"no_reprint"`
|
||||||
|
Autoplay int `json:"autoplay"`
|
||||||
|
UgcPay int `json:"ugc_pay"`
|
||||||
|
IsCooperation int `json:"is_cooperation"`
|
||||||
|
UgcPayPreview int `json:"ugc_pay_preview"`
|
||||||
|
NoBackground int `json:"no_background"`
|
||||||
|
CleanMode int `json:"clean_mode"`
|
||||||
|
IsSteinGate int `json:"is_stein_gate"`
|
||||||
|
Is360 int `json:"is_360"`
|
||||||
|
NoShare int `json:"no_share"`
|
||||||
|
ArcPay int `json:"arc_pay"`
|
||||||
|
FreeWatch int `json:"free_watch"`
|
||||||
|
} `json:"rights"`
|
||||||
|
Owner struct {
|
||||||
|
Mid int `json:"mid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Face string `json:"face"`
|
||||||
|
} `json:"owner"`
|
||||||
|
Stat struct {
|
||||||
|
Aid int `json:"aid"`
|
||||||
|
View int `json:"view"`
|
||||||
|
Danmaku int `json:"danmaku"`
|
||||||
|
Reply int `json:"reply"`
|
||||||
|
Favorite int `json:"favorite"`
|
||||||
|
Coin int `json:"coin"`
|
||||||
|
Share int `json:"share"`
|
||||||
|
NowRank int `json:"now_rank"`
|
||||||
|
HisRank int `json:"his_rank"`
|
||||||
|
Like int `json:"like"`
|
||||||
|
Dislike int `json:"dislike"`
|
||||||
|
Evaluation string `json:"evaluation"`
|
||||||
|
Vt int `json:"vt"`
|
||||||
|
} `json:"stat"`
|
||||||
|
ArgueInfo struct {
|
||||||
|
ArgueMsg string `json:"argue_msg"`
|
||||||
|
ArgueType int `json:"argue_type"`
|
||||||
|
ArgueLink string `json:"argue_link"`
|
||||||
|
} `json:"argue_info"`
|
||||||
|
Dynamic string `json:"dynamic"`
|
||||||
|
Cid int `json:"cid"`
|
||||||
|
Dimension struct {
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Rotate int `json:"rotate"`
|
||||||
|
} `json:"dimension"`
|
||||||
|
Premiere interface{} `json:"premiere"`
|
||||||
|
TeenageMode int `json:"teenage_mode"`
|
||||||
|
IsChargeableSeason bool `json:"is_chargeable_season"`
|
||||||
|
IsStory bool `json:"is_story"`
|
||||||
|
IsUpowerExclusive bool `json:"is_upower_exclusive"`
|
||||||
|
IsUpowerPlay bool `json:"is_upower_play"`
|
||||||
|
IsUpowerPreview bool `json:"is_upower_preview"`
|
||||||
|
EnableVt int `json:"enable_vt"`
|
||||||
|
VtDisplay string `json:"vt_display"`
|
||||||
|
NoCache bool `json:"no_cache"`
|
||||||
|
Pages []struct {
|
||||||
|
Cid int `json:"cid"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
From string `json:"from"`
|
||||||
|
Part string `json:"part"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
Vid string `json:"vid"`
|
||||||
|
Weblink string `json:"weblink"`
|
||||||
|
Dimension struct {
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Rotate int `json:"rotate"`
|
||||||
|
} `json:"dimension"`
|
||||||
|
FirstFrame string `json:"first_frame"`
|
||||||
|
} `json:"pages"`
|
||||||
|
Subtitle struct {
|
||||||
|
AllowSubmit bool `json:"allow_submit"`
|
||||||
|
List []struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Lan string `json:"lan"`
|
||||||
|
LanDoc string `json:"lan_doc"`
|
||||||
|
IsLock bool `json:"is_lock"`
|
||||||
|
SubtitleUrl string `json:"subtitle_url"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
IdStr string `json:"id_str"`
|
||||||
|
AiType int `json:"ai_type"`
|
||||||
|
AiStatus int `json:"ai_status"`
|
||||||
|
Author struct {
|
||||||
|
Mid int `json:"mid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Sex string `json:"sex"`
|
||||||
|
Face string `json:"face"`
|
||||||
|
Sign string `json:"sign"`
|
||||||
|
Rank int `json:"rank"`
|
||||||
|
Birthday int `json:"birthday"`
|
||||||
|
IsFakeAccount int `json:"is_fake_account"`
|
||||||
|
IsDeleted int `json:"is_deleted"`
|
||||||
|
InRegAudit int `json:"in_reg_audit"`
|
||||||
|
IsSeniorMember int `json:"is_senior_member"`
|
||||||
|
} `json:"author"`
|
||||||
|
} `json:"list"`
|
||||||
|
} `json:"subtitle"`
|
||||||
|
Label struct {
|
||||||
|
Type int `json:"type"`
|
||||||
|
} `json:"label"`
|
||||||
|
IsSeasonDisplay bool `json:"is_season_display"`
|
||||||
|
UserGarb struct {
|
||||||
|
UrlImageAniCut string `json:"url_image_ani_cut"`
|
||||||
|
} `json:"user_garb"`
|
||||||
|
HonorReply struct {
|
||||||
|
Honor []struct {
|
||||||
|
Aid int `json:"aid"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
|
WeeklyRecommendNum int `json:"weekly_recommend_num"`
|
||||||
|
} `json:"honor"`
|
||||||
|
} `json:"honor_reply"`
|
||||||
|
LikeIcon string `json:"like_icon"`
|
||||||
|
NeedJumpBv bool `json:"need_jump_bv"`
|
||||||
|
DisableShowUpInfo bool `json:"disable_show_up_info"`
|
||||||
|
IsStoryPlay int `json:"is_story_play"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
type VideoInfo struct {
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Face string `json:"face"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
|
Duration string `json:"duration"`
|
||||||
|
Pic string `json:"pic"`
|
||||||
|
Coin int `json:"coin"`
|
||||||
|
Danmaku int `json:"danmaku"`
|
||||||
|
Favorite int `json:"favorite"`
|
||||||
|
Like int `json:"like"`
|
||||||
|
Reply int `json:"reply"`
|
||||||
|
Share int `json:"share"`
|
||||||
|
View int `json:"view"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func secondsToTimeString(seconds int) string {
|
||||||
|
duration := time.Duration(seconds) * time.Second
|
||||||
|
|
||||||
|
if duration.Hours() < 1 {
|
||||||
|
return fmt.Sprintf("%02d:%02d", int(duration.Minutes()), int(duration.Seconds())%60)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%02d:%02d:%02d", int(duration.Hours()), int(duration.Minutes())%60, int(duration.Seconds())%60)
|
||||||
|
}
|
||||||
|
func GetVideoData(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
http.Error(w, "参数解析错误", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
bv_id := r.Form.Get("bvid")
|
||||||
|
resp, err := http.Get("https://api.bilibili.com/x/web-interface/view?bvid=" + bv_id)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "无法获取视频数据", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var video VideoData
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&video) // 使用json.NewDecoder解码JSON数据
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "无法解析视频数据", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info := &VideoInfo{
|
||||||
|
Owner: video.Data.Owner.Name,
|
||||||
|
Face: video.Data.Owner.Face,
|
||||||
|
Title: video.Data.Title,
|
||||||
|
Desc: video.Data.Desc,
|
||||||
|
Duration: secondsToTimeString(video.Data.Duration),
|
||||||
|
Pic: video.Data.Pic,
|
||||||
|
Coin: video.Data.Stat.Coin,
|
||||||
|
Danmaku: video.Data.Stat.Danmaku,
|
||||||
|
Favorite: video.Data.Stat.Favorite,
|
||||||
|
Like: video.Data.Stat.Like,
|
||||||
|
Reply: video.Data.Stat.Reply,
|
||||||
|
Share: video.Data.Stat.Share,
|
||||||
|
View: video.Data.Stat.View,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果需要返回JSON数据给客户端,可以使用以下代码
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
err = json.NewEncoder(w).Encode(info)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "无法解析视频数据", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
2
blog.go
2
blog.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"blog/api"
|
||||||
"blog/config"
|
"blog/config"
|
||||||
"blog/models"
|
"blog/models"
|
||||||
"blog/routes"
|
"blog/routes"
|
||||||
|
@ -16,6 +17,7 @@ func init() {
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
routes.InitRoute()
|
routes.InitRoute()
|
||||||
|
api.InitApi()
|
||||||
fmt.Printf("Version:v%v \n", config.Cfg.Version)
|
fmt.Printf("Version:v%v \n", config.Cfg.Version)
|
||||||
fmt.Printf("ListenAndServe On Port %v \n", config.Cfg.Port)
|
fmt.Printf("ListenAndServe On Port %v \n", config.Cfg.Port)
|
||||||
fmt.Printf("Dashboard On Path %v \n", config.Cfg.Dashboard)
|
fmt.Printf("Dashboard On Path %v \n", config.Cfg.Dashboard)
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"blog/config"
|
|
||||||
"blog/models"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Album(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
PhotoTemplate := models.Template.Album
|
|
||||||
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
PhotoTemplate.WriteError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
PhotoTemplate.WriteData(w, models.BuildViewData("Album", map[string]interface{}{
|
|
||||||
"memos_url": config.Cfg.MemosURL,
|
|
||||||
"memos_user": config.Cfg.MemosUser,
|
|
||||||
"memos_tag": config.Cfg.MemosAlbumTag,
|
|
||||||
}))
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/config"
|
||||||
|
"blog/models"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Memos(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
http.Error(w, "参数解析错误", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
fun := r.Form.Get("name")
|
||||||
|
log.Printf("R:%v,name:%s\n", r.Form, fun)
|
||||||
|
switch fun {
|
||||||
|
case "talk":
|
||||||
|
Template := models.Template.Talk
|
||||||
|
|
||||||
|
Template.WriteData(w, models.BuildViewData("Talk", map[string]interface{}{
|
||||||
|
"url": config.Cfg.MemosURL,
|
||||||
|
"user": config.Cfg.MemosUser,
|
||||||
|
"tag": config.Cfg.MemosTalkTag,
|
||||||
|
"author": config.Cfg.Author,
|
||||||
|
"qq": config.Cfg.Qq,
|
||||||
|
}))
|
||||||
|
case "album":
|
||||||
|
Template := models.Template.Album
|
||||||
|
tag := strings.Split(config.Cfg.MemosAlbumTag, ",")
|
||||||
|
Template.WriteData(w, models.BuildViewData("Album", map[string]interface{}{
|
||||||
|
"url": config.Cfg.MemosURL,
|
||||||
|
"user": config.Cfg.MemosUser,
|
||||||
|
"image": tag[0],
|
||||||
|
"video": tag[1],
|
||||||
|
}))
|
||||||
|
default:
|
||||||
|
http.Error(w, "无法解析页面请求"+fun, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"blog/config"
|
|
||||||
"blog/models"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Talk(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
TalkTemplate := models.Template.Talk
|
|
||||||
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
TalkTemplate.WriteError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
TalkTemplate.WriteData(w, models.BuildViewData("Talk", map[string]interface{}{
|
|
||||||
"memos_url": config.Cfg.MemosURL,
|
|
||||||
"memos_user": config.Cfg.MemosUser,
|
|
||||||
"memos_tag": config.Cfg.MemosTalkTag,
|
|
||||||
"author": config.Cfg.Author,
|
|
||||||
"qq": config.Cfg.Qq,
|
|
||||||
}))
|
|
||||||
|
|
||||||
}
|
|
|
@ -13,8 +13,7 @@ func InitRoute() {
|
||||||
http.HandleFunc("/categories", controller.Category)
|
http.HandleFunc("/categories", controller.Category)
|
||||||
http.HandleFunc("/article", controller.Article)
|
http.HandleFunc("/article", controller.Article)
|
||||||
http.HandleFunc("/extra-nav", controller.ExtraNav)
|
http.HandleFunc("/extra-nav", controller.ExtraNav)
|
||||||
http.HandleFunc("/album", controller.Album)
|
http.HandleFunc("/memos", controller.Memos)
|
||||||
http.HandleFunc("/talk", controller.Talk)
|
|
||||||
|
|
||||||
http.HandleFunc(config.Cfg.GitHookUrl, controller.GithubHook)
|
http.HandleFunc(config.Cfg.GitHookUrl, controller.GithubHook)
|
||||||
http.HandleFunc(config.Cfg.Dashboard, controller.Dashboard)
|
http.HandleFunc(config.Cfg.Dashboard, controller.Dashboard)
|
||||||
|
|
|
@ -32,9 +32,7 @@
|
||||||
@keyframes fadeIn{0% {opacity: 0;}100%{opacity: 1;}}
|
@keyframes fadeIn{0% {opacity: 0;}100%{opacity: 1;}}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
if (1) {
|
fetch('{{ .Data.url }}/api/v1/memo?creatorId={{ .Data.user }}&tag={{ .Data.tag }}').then(res => res.json()).then(data => {
|
||||||
let url = '{{ .Data.memos_url }}' // 修改api
|
|
||||||
fetch(url + '/api/v1/memo?creatorId={{ .Data.memos_user }}&tag={{ .Data.memos_tag }}').then(res => res.json()).then(data => {
|
|
||||||
let html = '',
|
let html = '',
|
||||||
imgs = []
|
imgs = []
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
|
@ -66,8 +64,218 @@
|
||||||
imgStatus.watch('.photo-img', () => { waterfall('.gallery-photos') })
|
imgStatus.watch('.photo-img', () => { waterfall('.gallery-photos') })
|
||||||
window.Lately && Lately.init({ target: '.photo-time' })
|
window.Lately && Lately.init({ target: '.photo-time' })
|
||||||
}).catch()
|
}).catch()
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
#bilibili {
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#bilibili .loading {
|
||||||
|
/*vertical-align: middle;*/
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: var(--primary, #673ab7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#bilibili .loading img {
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box {
|
||||||
|
display: flex;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid #e0e3ed;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--primary, #673ab7) !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
transition: 0.3s;
|
||||||
|
maxWidth768()
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box:hover {
|
||||||
|
border-color: #4976f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_cover {
|
||||||
|
width: 234px;
|
||||||
|
position: relative;
|
||||||
|
maxWidth768()
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_cover img {
|
||||||
|
width: 100%;
|
||||||
|
filter: none;
|
||||||
|
margin: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_cover .play_icon {
|
||||||
|
position: absolute;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
opacity: 0.8;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_cover span {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
right: 5px;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 1px 3px #7a7a7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info {
|
||||||
|
padding: 10px 10px 10px 18px;
|
||||||
|
line-height: 1;
|
||||||
|
width: calc(100% - 200px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
maxWidth768()
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .desc {
|
||||||
|
font-size: 15px;
|
||||||
|
margin: 2px 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
maxWidth768()
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .stat {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .stat svg {
|
||||||
|
margin-right: 3px;
|
||||||
|
font-size: 18px;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .stat svg path {
|
||||||
|
fill: var(--primary, #673ab7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .stat span {
|
||||||
|
margin-right: 10px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .owner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .owner .tip {
|
||||||
|
color: #f69;
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 3px 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bilibili_box .bilibili_info .owner img {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
object-fit: cover;
|
||||||
|
margin: 0 5px 0 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='light'] .bilibili_box .bilibili_info .stat svg, [data-theme='dark'] .bilibili_cover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="bilibili">
|
||||||
|
<div class='loading'><img src="/public/img/loading.svg" alt="加载中...">
|
||||||
|
<a>加载中...</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let playIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none" class="icon"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.67735 4.2798C5.98983 4.1725 7.85812 4.0625 10 4.0625C12.1421 4.0625 14.0105 4.17252 15.323 4.27983C16.2216 4.3533 16.9184 5.04049 16.9989 5.9318C17.0962 7.00837 17.1875 8.43614 17.1875 10C17.1875 11.5639 17.0962 12.9916 16.9989 14.0682C16.9184 14.9595 16.2216 15.6467 15.323 15.7202C14.0105 15.8275 12.1421 15.9375 10 15.9375C7.85812 15.9375 5.98983 15.8275 4.67735 15.7202C3.77861 15.6467 3.08174 14.9593 3.00119 14.0678C2.90388 12.9908 2.8125 11.5627 2.8125 10C2.8125 8.43727 2.90388 7.00924 3.00119 5.93221C3.08174 5.04067 3.77861 4.35327 4.67735 4.2798ZM10 2.8125C7.81674 2.8125 5.9136 2.92456 4.5755 3.03395C3.07738 3.15643 1.8921 4.31616 1.75626 5.81973C1.65651 6.92379 1.5625 8.39058 1.5625 10C1.5625 11.6094 1.65651 13.0762 1.75626 14.1803C1.8921 15.6838 3.07738 16.8436 4.5755 16.966C5.9136 17.0754 7.81674 17.1875 10 17.1875C12.1835 17.1875 14.0868 17.0754 15.4249 16.966C16.9228 16.8436 18.108 15.6841 18.2438 14.1807C18.3435 13.077 18.4375 11.6105 18.4375 10C18.4375 8.38948 18.3435 6.92296 18.2438 5.81931C18.108 4.31588 16.9228 3.15645 15.4249 3.03398C14.0868 2.92458 12.1835 2.8125 10 2.8125ZM12.1876 10.722C12.7431 10.4013 12.7431 9.59941 12.1876 9.27866L9.06133 7.47373C8.50577 7.15298 7.81133 7.55392 7.81133 8.19542V11.8053C7.81133 12.4468 8.50577 12.8477 9.06133 12.527L12.1876 10.722Z" fill="#9499A0"/></svg>`
|
||||||
|
let likeIcon = `<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" class="icon"><path fill-rule="evenodd" clip-rule="evenodd" d="M9.77234 30.8573V11.7471H7.54573C5.50932 11.7471 3.85742 13.3931 3.85742 15.425V27.1794C3.85742 29.2112 5.50932 30.8573 7.54573 30.8573H9.77234ZM11.9902 30.8573V11.7054C14.9897 10.627 16.6942 7.8853 17.1055 3.33591C17.2666 1.55463 18.9633 0.814421 20.5803 1.59505C22.1847 2.36964 23.243 4.32583 23.243 6.93947C23.243 8.50265 23.0478 10.1054 22.6582 11.7471H29.7324C31.7739 11.7471 33.4289 13.402 33.4289 15.4435C33.4289 15.7416 33.3928 16.0386 33.3215 16.328L30.9883 25.7957C30.2558 28.7683 27.5894 30.8573 24.528 30.8573H11.9911H11.9902Z"></path></svg>`
|
||||||
|
let coinIcon = `<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" class="icon" style="fill:;"><path fill-rule="evenodd" clip-rule="evenodd" d="M14.045 25.5454C7.69377 25.5454 2.54504 20.3967 2.54504 14.0454C2.54504 7.69413 7.69377 2.54541 14.045 2.54541C20.3963 2.54541 25.545 7.69413 25.545 14.0454C25.545 17.0954 24.3334 20.0205 22.1768 22.1771C20.0201 24.3338 17.095 25.5454 14.045 25.5454ZM9.66202 6.81624H18.2761C18.825 6.81624 19.27 7.22183 19.27 7.72216C19.27 8.22248 18.825 8.62807 18.2761 8.62807H14.95V10.2903C17.989 10.4444 20.3766 12.9487 20.3855 15.9916V17.1995C20.3854 17.6997 19.9799 18.1052 19.4796 18.1052C18.9793 18.1052 18.5738 17.6997 18.5737 17.1995V15.9916C18.5667 13.9478 16.9882 12.2535 14.95 12.1022V20.5574C14.95 21.0577 14.5444 21.4633 14.0441 21.4633C13.5437 21.4633 13.1382 21.0577 13.1382 20.5574V12.1022C11.1 12.2535 9.52148 13.9478 9.51448 15.9916V17.1995C9.5144 17.6997 9.10883 18.1052 8.60856 18.1052C8.1083 18.1052 7.70273 17.6997 7.70265 17.1995V15.9916C7.71158 12.9487 10.0992 10.4444 13.1382 10.2903V8.62807H9.66202C9.11309 8.62807 8.66809 8.22248 8.66809 7.72216C8.66809 7.22183 9.11309 6.81624 9.66202 6.81624Z"></path></svg>`
|
||||||
|
function Formatv(item) {
|
||||||
|
let ids,
|
||||||
|
content = item.content,
|
||||||
|
bilibilis = content.match(/{\s*bilibili\s*(.*)\s*}/g);
|
||||||
|
if (bilibilis) {
|
||||||
|
ids = bilibilis.map(item => item.replace(/{\s*bilibili\s*(.*)\s*}/, '$1').replace(/.*video\/([^\/]*).*/, '$1').trim());
|
||||||
|
}
|
||||||
|
content = content.replace(/#(.*?)\s/g, '').replace(/{.*}/g, '').replace(/\!\[(.*?)\]\((.*?)\)/g, '').replace(/```/g, '')
|
||||||
|
let text = content.replace(/\[(.*?)\]\((.*?)\)/g, '[链接]').trim();
|
||||||
|
return {content, ids, text}
|
||||||
|
}
|
||||||
|
fetch('{{ .Data.url }}/api/v1/memo?creatorId={{ .Data.user }}&tag={{ .Data.video }}').then(res => res.json()).then(async data => {
|
||||||
|
let items = [], html = ''
|
||||||
|
data.forEach(item => {
|
||||||
|
items.push(Formatv(item))
|
||||||
|
})
|
||||||
|
for (const item of items) {
|
||||||
|
html += `<div class="bilibili_item"><span>${item.text}</span>`
|
||||||
|
for (const id of item.ids) {
|
||||||
|
await fetch(`/api/bilibili?bvid=${id}`).then(res => res.json()).then(data => {
|
||||||
|
console.log(data)
|
||||||
|
html += `<a href="https://www.bilibili.com/video/${id}/" class="bilibili_box" id="${id}">
|
||||||
|
<div class="bilibili_cover">
|
||||||
|
<img src="https://s1.hdslb.com/bfs/static/player/img/play.svg" class="play_icon no-lazyload">
|
||||||
|
<img src="${data.pic + '&h=300'}" class="no-lazyload">
|
||||||
|
<span>${data.duration}</span>
|
||||||
|
</div>
|
||||||
|
<div class="bilibili_info">
|
||||||
|
<div class="title">${data.title}</div>
|
||||||
|
<div class="desc">${data.desc}</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span>${playIcon}${data.view}</span>
|
||||||
|
<span>${likeIcon}${data.like}</span>
|
||||||
|
<span>${coinIcon}${data.coin}</span>
|
||||||
|
</div>
|
||||||
|
<div class="owner">
|
||||||
|
<span class="tip">视频</span>
|
||||||
|
<img src="${data.face + '&h=100'}" class="no-lazyload">
|
||||||
|
<span>${data.owner}</span>
|
||||||
|
</div>
|
||||||
|
</div></a>`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
html += `</div>`
|
||||||
|
document.getElementById('bilibili').innerHTML = html
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<script defer src="https://cdn.jsdelivr.net/gh/jkjoy/14e/js/waterfall.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/gh/jkjoy/14e/js/waterfall.min.js"></script>
|
||||||
<script defer src="https://cdn.jsdelivr.net/gh/jkjoy/14e/js/imgStatus.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/gh/jkjoy/14e/js/imgStatus.min.js"></script>
|
||||||
|
|
|
@ -19,10 +19,10 @@
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||||
{{if ne .Config.MemosAlbumTag ""}}
|
{{if ne .Config.MemosAlbumTag ""}}
|
||||||
<li><a class="dropdown-item" href="/album">Album</a></li>
|
<li><a class="dropdown-item" href="/memos?name=album">Album</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{if ne .Config.MemosTalkTag ""}}
|
{{if ne .Config.MemosTalkTag ""}}
|
||||||
<li><a class="dropdown-item" href="/talk">Talk</a></li>
|
<li><a class="dropdown-item" href="/memos?name=talk">Talk</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
{{range $nav := .Navs }}
|
{{range $nav := .Navs }}
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
opacity: .9;
|
opacity: .9;
|
||||||
}
|
}
|
||||||
.talk_bottom .icon {
|
.talk_bottom .icon {
|
||||||
color: var(--font-color);
|
color: var(--primary,#673ab7);
|
||||||
float: right;
|
float: right;
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
}
|
}
|
||||||
|
@ -207,33 +207,12 @@
|
||||||
|
|
||||||
<div class="limit">- 只展示最近30条说说 -</div>
|
<div class="limit">- 只展示最近30条说说 -</div>
|
||||||
<script>
|
<script>
|
||||||
if(1) {
|
|
||||||
let url = '{{ .Data.memos_url }}'
|
|
||||||
fetch(url + '/api/v1/memo?creatorId={{ .Data.memos_user }}&tag={{ .Data.memos_tag }}&limit=30').then(res => res.json()).then(data => { // 注意修改域名和用户id
|
|
||||||
let items = [],
|
|
||||||
html = '',
|
|
||||||
icon = '<svg viewBox="0 0 512 512"xmlns="http://www.w3.org/2000/svg"class="is-badge icon"><path d="m512 268c0 17.9-4.3 34.5-12.9 49.7s-20.1 27.1-34.6 35.4c.4 2.7.6 6.9.6 12.6 0 27.1-9.1 50.1-27.1 69.1-18.1 19.1-39.9 28.6-65.4 28.6-11.4 0-22.3-2.1-32.6-6.3-8 16.4-19.5 29.6-34.6 39.7-15 10.2-31.5 15.2-49.4 15.2-18.3 0-34.9-4.9-49.7-14.9-14.9-9.9-26.3-23.2-34.3-40-10.3 4.2-21.1 6.3-32.6 6.3-25.5 0-47.4-9.5-65.7-28.6-18.3-19-27.4-42.1-27.4-69.1 0-3 .4-7.2 1.1-12.6-14.5-8.4-26-20.2-34.6-35.4-8.5-15.2-12.8-31.8-12.8-49.7 0-19 4.8-36.5 14.3-52.3s22.3-27.5 38.3-35.1c-4.2-11.4-6.3-22.9-6.3-34.3 0-27 9.1-50.1 27.4-69.1s40.2-28.6 65.7-28.6c11.4 0 22.3 2.1 32.6 6.3 8-16.4 19.5-29.6 34.6-39.7 15-10.1 31.5-15.2 49.4-15.2s34.4 5.1 49.4 15.1c15 10.1 26.6 23.3 34.6 39.7 10.3-4.2 21.1-6.3 32.6-6.3 25.5 0 47.3 9.5 65.4 28.6s27.1 42.1 27.1 69.1c0 12.6-1.9 24-5.7 34.3 16 7.6 28.8 19.3 38.3 35.1 9.5 15.9 14.3 33.4 14.3 52.4zm-266.9 77.1 105.7-158.3c2.7-4.2 3.5-8.8 2.6-13.7-1-4.9-3.5-8.8-7.7-11.4-4.2-2.7-8.8-3.6-13.7-2.9-5 .8-9 3.2-12 7.4l-93.1 140-42.9-42.8c-3.8-3.8-8.2-5.6-13.1-5.4-5 .2-9.3 2-13.1 5.4-3.4 3.4-5.1 7.7-5.1 12.9 0 5.1 1.7 9.4 5.1 12.9l58.9 58.9 2.9 2.3c3.4 2.3 6.9 3.4 10.3 3.4 6.7-.1 11.8-2.9 15.2-8.7z"fill="#1da1f2"></path></svg>';
|
|
||||||
data.forEach(item => { items.push(Format(item)) });
|
|
||||||
if (items.length == 30) document.querySelector('.limit').style.display = 'block';
|
|
||||||
items.forEach(item => {
|
|
||||||
html += `<div class="talk_item"><div class="talk_meta"><img class="no-lightbox no-lazyload avatar" src="https://q1.qlogo.cn/g?b=qq&nk={{ .Data.qq }}&s=5"><div class="info"><span class="talk_nick">{{ .Data.author }}${icon}</span><span class="talk_date">${item.date}</span></div></div><div class="talk_content">${item.content}</div><div class="talk_bottom"><div><span class="talk_tag"># ${item.tag}</span></div><a href="javascript:;"onclick="goComment('${item.text}')"><span class="icon"><i class="fa-solid fa-message fa-fw"></i></span></a></div></div>` // 注意修改头像链接和名称
|
|
||||||
})
|
|
||||||
document.getElementById('talk').innerHTML = html
|
|
||||||
})
|
|
||||||
// 页面评论
|
|
||||||
function goComment(e) {
|
|
||||||
var n = document.querySelector(".el-textarea__inner")
|
|
||||||
n.value = `> ${e}\n\n`;
|
|
||||||
n.focus();
|
|
||||||
btf.snackbarShow("无需删除空行,直接输入评论即可", !1, 2e3);
|
|
||||||
}
|
|
||||||
// 页面内容格式化
|
|
||||||
function Format(item) {
|
function Format(item) {
|
||||||
let date = getTime(new Date(item.createdTs * 1000).toString()),
|
let date = getTime(new Date(item.createdTs * 1000).toString()),
|
||||||
content = item.content,
|
content = item.content,
|
||||||
tag = item.content.match(/\{(.*?)\}/g),
|
|
||||||
imgs = content.match(/!\[.*\]\(.*?\)/g),
|
imgs = content.match(/!\[.*\]\(.*?\)/g),
|
||||||
text = ''
|
musics = content.match(/{\s*music\s*(.*)\s*}/g),
|
||||||
|
videos = content.match(/{\s*bilibili\s*(.*)\s*}/g);
|
||||||
if (imgs) imgs = imgs.map(item => { return item.replace(/!\[.*\]\((.*?)\)/, '$1') })
|
if (imgs) imgs = imgs.map(item => { return item.replace(/!\[.*\]\((.*?)\)/, '$1') })
|
||||||
if (item.resourceList.length) {
|
if (item.resourceList.length) {
|
||||||
if (!imgs) imgs = []
|
if (!imgs) imgs = []
|
||||||
|
@ -242,31 +221,50 @@
|
||||||
else imgs.push(`${url}/o/r/${t.id}/${t.publicId}/${t.filename}`)
|
else imgs.push(`${url}/o/r/${t.id}/${t.publicId}/${t.filename}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
text = content.replace(/#(.*?)\s/g, '').replace(/\!?\[(.*?)\]\((.*?)\)/g, '').replace(/\{(.*?)\}/g, '')
|
content = content.replace(/#(.*?)\s/g, '').replace(/{.*}/g, '').replace(/\!\[(.*?)\]\((.*?)\)/g, '').replace(/```/g, '')
|
||||||
content = text.replace(/\[(.*?)\]\((.*?)\)/g, `<a href="$2">@$1</a>`);
|
let text = content.replace(/\[(.*?)\]\((.*?)\)/g, '[链接]').trim();
|
||||||
|
content = content.replace(/\[(.*?)\]\((.*?)\)/g, `<a href="$2">@$1</a>`);
|
||||||
if (imgs) {
|
if (imgs) {
|
||||||
content += `<div class="zone_imgbox">`
|
content += `<div class="zone_imgbox">`
|
||||||
imgs.forEach(e => content += `<a href="${e}" data-fancybox="gallery" class="fancybox" data-thumb="${e}"><img class="no-lazyload" src="${e}"></a>` // 2023-02-06更新
|
imgs.forEach(e => content += `<a href="${e}" data-fancybox="gallery" class="fancybox" data-thumb="${e}"><img class="no-lazyload talk-img" src="${e}"></a>`)
|
||||||
)
|
|
||||||
content += '</div>'
|
content += '</div>'
|
||||||
}
|
}
|
||||||
return {
|
if (musics) musics.forEach(item => { content += `<meting-js auto="${item.replace(/{\s*music\s*(.*)\s*}/, '$1')}" theme="var(--leonus-main)" preload="metadata"></meting-js>` })
|
||||||
content: content,
|
if (videos) videos.forEach(item => {
|
||||||
tag: tag ? tag[0].replace(/\{(.*?)\}/,'$1') : '无标签',
|
content += `<div style="position: relative; padding: 30% 45%;margin-top: 10px;margin-bottom: 10px"><iframe style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;" src="//player.bilibili.com/player.html?autoplay=0&bvid=${item.replace(/{\s*bilibili\s*(.*)\s*}/, '$1').replace(/.*video\/([^\/]*).*/, '$1')}" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe></div>`
|
||||||
date: date,
|
})
|
||||||
text: text.replace(/\[(.*?)\]\((.*?)\)/g, '[链接]' + `${imgs?'[图片]':''}`)
|
return { content, date, text }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// 页面时间格式化
|
|
||||||
function getTime(time) {
|
function getTime(time) {
|
||||||
|
var nol = function (h) { return h > 9 ? h : '0' + h }
|
||||||
|
var now = new Date()
|
||||||
|
var yesterday = new Date(now.getTime() - (1000 * 60 * 60 * 24)).toLocaleDateString()
|
||||||
|
var twoDaysAgo = new Date(now.getTime() - 2 * (1000 * 60 * 60 * 24)).toLocaleDateString()
|
||||||
let d = new Date(time),
|
let d = new Date(time),
|
||||||
ls = [d.getFullYear(), d.getMonth() + 1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()];
|
ls = [d.getFullYear(), nol(d.getMonth() + 1), nol(d.getDate()), nol(d.getHours()), nol(d.getMinutes()), nol(d.getSeconds())],
|
||||||
for (let i = 0; i < ls.length; i++) {
|
day = d.toLocaleDateString()
|
||||||
ls[i] = ls[i] <= 9 ? '0' + ls[i] : ls[i] + ''
|
if (day === now.toLocaleDateString()) {
|
||||||
}
|
return '今天 ' + ls[3] + ':' + ls[4]
|
||||||
if (new Date().getFullYear() == ls[0]) return ls[1] + '月' + ls[2] + '日 ' + ls[3] +':'+ ls[4]
|
} else if (day === yesterday) {
|
||||||
|
return '昨天 ' + ls[3] + ':' + ls[4]
|
||||||
|
} else if (day === twoDaysAgo) {
|
||||||
|
return '前天 ' + ls[3] + ':' + ls[4]
|
||||||
|
} else {
|
||||||
|
if (now.getFullYear() == ls[0]) return ls[1] + '月' + ls[2] + '日 ' + ls[3] + ':' + ls[4]
|
||||||
else return ls[0] + '年' + ls[1] + '月' + ls[2] + '日 ' + ls[3] + ':' + ls[4]
|
else return ls[0] + '年' + ls[1] + '月' + ls[2] + '日 ' + ls[3] + ':' + ls[4]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fetch('{{ .Data.url }}/api/v1/memo?creatorId={{ .Data.user }}&tag={{ .Data.tag }}&limit=30').then(res => res.json()).then(data => {
|
||||||
|
let items = [], html = ''
|
||||||
|
data.forEach(item => { items.push(Format(item)) })
|
||||||
|
if (items.length == 30) document.querySelector('.limit').style.display = 'block'
|
||||||
|
items.forEach(item => {
|
||||||
|
html += `<div class="talk_item"><div class="talk_meta"><img class="no-lightbox no-lazyload avatar" src="https://q1.qlogo.cn/g?b=qq&nk={{ .Data.qq }}&s=5"><div class="info"><span class="talk_nick">Leonus</span><span class="talk_date">${item.date}</span></div><a href="javascript:;" class="goComment" onclick="goComment('${item.text}')"><span class="icon"><i class="fa-solid fa-message fa-fw"></i></span></a></div><div class="talk_content">${item.content}</div></div>`
|
||||||
|
})
|
||||||
|
document.getElementById('talk').innerHTML = html
|
||||||
|
imgStatus.watch('.talk-img', () => { waterfall('#talk') })
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/gh/jkjoy/14e/js/waterfall.min.js"></script>
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/gh/jkjoy/14e/js/imgStatus.min.js"></script>
|
||||||
{{template "footer" .}}
|
{{template "footer" .}}
|
||||||
|
|
Loading…
Reference in New Issue