Skip to content

Commit

Permalink
feat: add common driver service abstraction.
Browse files Browse the repository at this point in the history
  • Loading branch information
syhily committed Nov 15, 2022
1 parent d6b3a5c commit ea7150f
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 48 deletions.
53 changes: 53 additions & 0 deletions internal/driver/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package driver

import (
"errors"
"fmt"
"io"

"github.com/bookstairs/bookhunter/internal/client"
)

type (
// Source is a net drive disk provider.
Source string

// Share is an atomic downloadable file.
Share struct {
// FileName is a file name with the file extension.
FileName string
// URL is the downloadable url for this file.
URL string
// Properties could be some metadata, such as the token for this downloadable share.
Properties map[string]string
}

// Driver is used to resolve the links from a Source.
Driver interface {
// Resolve the given link and return the file name with the download link.
Resolve(shareLink string, passcode string) []Share

// Download the given link.
Download(share Share) io.ReadCloser
}
)

const (
ALIYUN Source = "aliyun"
LANZOU Source = "lanzou"
TELECOM Source = "telecom"
)

// New will create the basic driver service.
func New(source Source, config *client.Config) (Driver, error) {
switch source {
case ALIYUN:
return nil, errors.New("we don't support aliyun currently")
case TELECOM:
return nil, errors.New("we don't support telecom currently")
case LANZOU:
return nil, errors.New("we don't support lanzou currently")
default:
return nil, fmt.Errorf("invalid driver service %s", source)
}
}
27 changes: 14 additions & 13 deletions internal/fetcher/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/yi-ge/unzip"

"github.com/bookstairs/bookhunter/internal/driver"
"github.com/bookstairs/bookhunter/internal/log"
"github.com/bookstairs/bookhunter/internal/naming"
"github.com/bookstairs/bookhunter/internal/progress"
Expand Down Expand Up @@ -102,8 +103,8 @@ func (f *commonFetcher) startDownload() {
formats = f.filterFormats(formats)

// Download the file by formats one by one.
for format, url := range formats {
err := f.downloadFile(bookID, format, url)
for format, share := range formats {
err := f.downloadFile(bookID, format, share)
if err != nil {
f.finishDownload(err)
break
Expand All @@ -120,8 +121,8 @@ func (f *commonFetcher) startDownload() {
}

// downloadFile in a thread.
func (f *commonFetcher) downloadFile(bookID int64, format Format, url string) error {
file, err := f.service.fetch(bookID, format, url)
func (f *commonFetcher) downloadFile(bookID int64, format Format, share driver.Share) error {
file, err := f.service.fetch(bookID, format, share)
if err != nil {
return err
}
Expand All @@ -130,17 +131,17 @@ func (f *commonFetcher) downloadFile(bookID int64, format Format, url string) er
// Rename if it was required.
prefix := strconv.FormatInt(bookID, 10)
if f.Rename {
file.name = prefix + "." + string(format)
share.FileName = prefix + "." + string(format)
} else {
file.name = prefix + "_" + file.name
share.FileName = prefix + "_" + share.FileName
}

// Escape the file name for avoiding the illegal characters.
// Ref: https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
file.name = naming.EscapeFilename(file.name)
share.FileName = naming.EscapeFilename(share.FileName)

// Generate the file path.
path := filepath.Join(f.DownloadPath, file.name)
path := filepath.Join(f.DownloadPath, share.FileName)

// Remove the exist file.
if _, err := os.Stat(path); err == nil {
Expand All @@ -157,7 +158,7 @@ func (f *commonFetcher) downloadFile(bookID int64, format Format, url string) er
defer func() { _ = writer.Close() }()

// Add download progress.
bar := log.NewProgressBar(bookID, f.progress.Size(), file.name, file.size)
bar := log.NewProgressBar(bookID, f.progress.Size(), share.FileName, file.size)
defer func() { _ = bar.Close() }()

// Write file content
Expand All @@ -176,12 +177,12 @@ func (f *commonFetcher) downloadFile(bookID int64, format Format, url string) er
}

// filterFormats will find the valid formats by user configure.
func (f *commonFetcher) filterFormats(formats map[Format]string) map[Format]string {
fs := make(map[Format]string)
for format, url := range formats {
func (f *commonFetcher) filterFormats(formats map[Format]driver.Share) map[Format]driver.Share {
fs := make(map[Format]driver.Share)
for format, share := range formats {
for _, vf := range f.Formats {
if format == vf {
fs[format] = url
fs[format] = share
break
}
}
Expand Down
5 changes: 3 additions & 2 deletions internal/fetcher/sanqiu.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"

"github.com/bookstairs/bookhunter/internal/client"
"github.com/bookstairs/bookhunter/internal/driver"
"github.com/bookstairs/bookhunter/internal/sanqiu"
)

Expand Down Expand Up @@ -55,12 +56,12 @@ func (s *sanqiuService) size() (int64, error) {
return books[0].ID, nil
}

func (s *sanqiuService) formats(id int64) (map[Format]string, error) {
func (s *sanqiuService) formats(id int64) (map[Format]driver.Share, error) {
// TODO implement me
panic("implement me")
}

func (s *sanqiuService) fetch(id int64, format Format, url string) (*fetch, error) {
func (s *sanqiuService) fetch(id int64, format Format, share driver.Share) (*fetch, error) {
// TODO implement me
panic("implement me")
}
10 changes: 3 additions & 7 deletions internal/fetcher/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/go-resty/resty/v2"

"github.com/bookstairs/bookhunter/internal/naming"
"github.com/bookstairs/bookhunter/internal/driver"
)

// service is a real implementation for fetcher.
Expand All @@ -16,25 +16,21 @@ type service interface {
size() (int64, error)

// formats will query the available downloadable file formats.
formats(id int64) (map[Format]string, error)
formats(int64) (map[Format]driver.Share, error)

// fetch the given book ID.
fetch(id int64, format Format, url string) (*fetch, error)
fetch(int64, Format, driver.Share) (*fetch, error)
}

// fetch is the result which can be created from a resty.Response
type fetch struct {
name string
content io.ReadCloser
size int64
}

// createFetch will try to create a fetch. Remember to create the response with the `SetDoNotParseResponse` option.
func createFetch(resp *resty.Response) *fetch {
// Try to parse a filename if it existed.
name := naming.Filename(resp)
return &fetch{
name: name,
content: resp.RawBody(),
size: resp.RawResponse.ContentLength,
}
Expand Down
18 changes: 12 additions & 6 deletions internal/fetcher/talebook.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package fetcher

import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"

"github.com/bookstairs/bookhunter/internal/client"
"github.com/bookstairs/bookhunter/internal/driver"
"github.com/bookstairs/bookhunter/internal/log"
"github.com/bookstairs/bookhunter/internal/talebook"
)
Expand Down Expand Up @@ -98,7 +101,7 @@ func (t *talebookService) size() (int64, error) {
return bookID, nil
}

func (t *talebookService) formats(id int64) (map[Format]string, error) {
func (t *talebookService) formats(id int64) (map[Format]driver.Share, error) {
resp, err := t.client.R().
SetResult(&talebook.BookResp{}).
SetPathParam("bookID", strconv.FormatInt(id, 10)).
Expand All @@ -110,13 +113,16 @@ func (t *talebookService) formats(id int64) (map[Format]string, error) {
result := resp.Result().(*talebook.BookResp)
switch result.Err {
case talebook.SuccessStatus:
formats := make(map[Format]string)
formats := make(map[Format]driver.Share)
for _, file := range result.Book.Files {
format, err := ParseFormat(file.Format)
format, err := ParseFormat(strings.ToLower(file.Format))
if err != nil {
return nil, err
}
formats[format] = file.Href
formats[format] = driver.Share{
FileName: fmt.Sprintf("%s.%s", result.Book.Title, format),
URL: file.Href,
}
}
return formats, nil
case talebook.BookNotFoundStatus:
Expand All @@ -126,10 +132,10 @@ func (t *talebookService) formats(id int64) (map[Format]string, error) {
}
}

func (t *talebookService) fetch(_ int64, _ Format, url string) (*fetch, error) {
func (t *talebookService) fetch(_ int64, _ Format, share driver.Share) (*fetch, error) {
resp, err := t.client.R().
SetDoNotParseResponse(true).
Get(url)
Get(share.URL)
if err != nil {
return nil, err
}
Expand Down
6 changes: 4 additions & 2 deletions internal/fetcher/telegram.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package fetcher

import "github.com/bookstairs/bookhunter/internal/driver"

type telegramService struct {
config *Config
}
Expand All @@ -14,10 +16,10 @@ func (s *telegramService) size() (int64, error) {
panic("implement me")
}

func (s *telegramService) formats(id int64) (map[Format]string, error) {
func (s *telegramService) formats(id int64) (map[Format]driver.Share, error) {
panic("implement me")
}

func (s *telegramService) fetch(id int64, format Format, url string) (*fetch, error) {
func (s *telegramService) fetch(id int64, format Format, share driver.Share) (*fetch, error) {
panic("implement me")
}
18 changes: 0 additions & 18 deletions internal/naming/naming.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package naming

import (
"mime"
"net/url"
"strings"
"unicode"

"github.com/go-resty/resty/v2"
)

func isLetter(s string) bool {
Expand Down Expand Up @@ -34,18 +31,3 @@ func Extension(link string) (string, bool) {
}
return "", false
}

// Filename parse the file name from Content-Disposition header.
// If there is no such head, we would return blank string.
func Filename(resp *resty.Response) (name string) {
header := resp.Header()
if disposition := header.Get("Content-Disposition"); disposition != "" {
if _, params, err := mime.ParseMediaType(disposition); err == nil {
if filename, ok := params["filename"]; ok {
name = EscapeFilename(filename)
}
}
}

return
}

0 comments on commit ea7150f

Please sign in to comment.