feat(app): add internal components batch 2
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type embyChapter struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Number int `json:"number"`
|
||||
}
|
||||
|
||||
func embyFolderName(drama Drama) string {
|
||||
folder := []rune(safeFilename(drama.DisplayTitle()))
|
||||
for len(string(folder)) > 180 {
|
||||
folder = folder[:len(folder)-1]
|
||||
}
|
||||
identity := sha256.Sum256([]byte(drama.ID))
|
||||
return string(folder) + " [" + hex.EncodeToString(identity[:8]) + "]"
|
||||
}
|
||||
|
||||
func embyChapterRecords(id string, chapters []Chapter, previous []embyChapter) ([]embyChapter, error) {
|
||||
if len(chapters) == 0 && len(previous) == 0 || len(chapters) > 2000 || len(previous) > 2000 {
|
||||
return nil, errors.New("可导出分集数无效")
|
||||
}
|
||||
byID := make(map[string]embyChapter, len(previous)+len(chapters))
|
||||
used := make(map[int]bool, len(previous)+len(chapters))
|
||||
for _, chapter := range previous {
|
||||
if !validEmbyIdentity(id, chapter.ID) || chapter.Number < 1 || chapter.Number > 2000 || used[chapter.Number] || byID[chapter.ID].ID != "" {
|
||||
return nil, errors.New("已有 Emby 分集清单无效,原文件已保留")
|
||||
}
|
||||
byID[chapter.ID], used[chapter.Number] = chapter, true
|
||||
}
|
||||
seen := make(map[string]bool, len(chapters))
|
||||
for index, chapter := range chapters {
|
||||
if !validEmbyIdentity(id, chapter.ID) || seen[chapter.ID] {
|
||||
return nil, errors.New("站源未提供唯一、稳定的分集 ID,暂不能导出")
|
||||
}
|
||||
seen[chapter.ID] = true
|
||||
record := byID[chapter.ID]
|
||||
if record.ID == "" {
|
||||
number := index + 1
|
||||
for used[number] && number <= 2000 {
|
||||
number++
|
||||
}
|
||||
if number > 2000 {
|
||||
return nil, errors.New("Emby 分集数超过 2000 集")
|
||||
}
|
||||
record = embyChapter{ID: chapter.ID, Number: number}
|
||||
used[number] = true
|
||||
}
|
||||
record.Title = firstNonEmpty(chapter.Title, "第 "+chapter.EpisodeString(index+1)+" 集")
|
||||
byID[chapter.ID] = record
|
||||
}
|
||||
result := make([]embyChapter, 0, len(byID))
|
||||
for _, chapter := range byID {
|
||||
result = append(result, chapter)
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool { return result[i].Number < result[j].Number })
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func writeEmbyFiles(drama Drama, chapters []embyChapter, base string, key []byte, owner string, write func(string, []byte) error, merged ...*embyMergedRecord) error {
|
||||
title := drama.DisplayTitle()
|
||||
type nfoValue struct {
|
||||
Value string `xml:",chardata"`
|
||||
Type string `xml:"type,attr,omitempty"`
|
||||
Aspect string `xml:"aspect,attr,omitempty"`
|
||||
}
|
||||
var thumb *nfoValue
|
||||
if cover := embyCoverURL(drama, base, key, owner); cover != "" {
|
||||
thumb = &nfoValue{Value: cover, Aspect: "poster"}
|
||||
}
|
||||
show := struct {
|
||||
XMLName xml.Name `xml:"tvshow"`
|
||||
Title string `xml:"title"`
|
||||
Plot string `xml:"plot,omitempty"`
|
||||
Identity nfoValue `xml:"uniqueid"`
|
||||
Thumb *nfoValue `xml:"thumb,omitempty"`
|
||||
}{Title: title, Plot: firstNonEmpty(drama.Desc, drama.Intro), Identity: nfoValue{Value: embyMetadataID(key, drama.ID), Type: "juku"}, Thumb: thumb}
|
||||
body, err := xml.MarshalIndent(show, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = write("tvshow.nfo", append([]byte(xml.Header), body...)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, chapter := range chapters {
|
||||
query := url.Values{"id": {drama.ID}, "chapter": {chapter.ID}, "key": {embyToken(key, drama.ID, chapter.ID, owner)}}
|
||||
if owner != "" {
|
||||
query.Set("account", owner)
|
||||
}
|
||||
path := fmt.Sprintf("Season 01/S01E%03d", chapter.Number)
|
||||
episode := struct {
|
||||
XMLName xml.Name `xml:"episodedetails"`
|
||||
Title string `xml:"title"`
|
||||
Show string `xml:"showtitle"`
|
||||
Season int `xml:"season"`
|
||||
Episode int `xml:"episode"`
|
||||
}{Title: chapter.Title, Show: title, Season: 1, Episode: chapter.Number}
|
||||
body, err = xml.MarshalIndent(episode, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = write(path+".nfo", append([]byte(xml.Header), body...)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = write(path+".strm", []byte(base+"/api/emby/stream.m3u8?"+query.Encode()+"\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(merged) > 0 && merged[0] != nil {
|
||||
return writeEmbyMerged(drama, merged[0], base, key, owner, write)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user