Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions drivers/streamtape/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ func (d *Streamtape) Put(ctx context.Context, dstDir model.Obj, file model.FileS
if folderID != "" && folderID != "0" {
params["folder"] = folderID
}
if d.Sha256 != "" {
params["sha256"] = d.Sha256
}

var uploadURL uploadURLResult
if err := d.callAPI(ctx, "/file/ul", params, &uploadURL); err != nil {
Expand Down Expand Up @@ -381,6 +384,35 @@ func (d *Streamtape) Put(ctx context.Context, dstDir model.Obj, file model.FileS
}, nil
}

// PutURL initiates a remote upload from an external URL
func (d *Streamtape) PutURL(ctx context.Context, dstDir model.Obj, name, url string) (model.Obj, error) {
folderID := d.RootFolderID
if dstDir.GetID() != "" {
folderID = folderIDFromObjID(dstDir.GetID())
}

params := map[string]string{
"url": url,
}
if folderID != "" && folderID != "0" {
params["folder"] = folderID
}
if name != "" {
params["name"] = name
}

var result remoteDlAddResult
if err := d.callAPI(ctx, "/remotedl/add", params, &result); err != nil {
return nil, err
}

return &model.Object{
ID: encodeRemoteUploadID(result.ID),
Name: name,
IsFolder: false,
}, nil
}

func (d *Streamtape) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
return nil, errs.NotImplement
}
Expand All @@ -397,4 +429,115 @@ func (d *Streamtape) ArchiveDecompress(ctx context.Context, srcObj, dstDir model
return nil, errs.NotImplement
}

func (d *Streamtape) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
switch strings.ToLower(args.Method) {
case "remotedl_status":
return d.remoteDlStatus(ctx, args)
case "remotedl_remove":
return d.remoteDlRemove(ctx, args)
case "file_info":
return d.fileInfo(ctx, args)
case "thumbnail":
return d.thumbnail(ctx, args)
case "conversion_status":
return d.conversionStatus(ctx, args)
default:
return nil, errs.NotSupport
}
}

func (d *Streamtape) extractRemoteUploadID(args model.OtherArgs) (string, error) {
uploadID := remoteUploadIDFromObjID(args.Obj.GetID())
if uploadID == "" {
if data, ok := args.Data.(map[string]interface{}); ok {
if id, ok := data["id"].(string); ok {
uploadID = id
}
}
}
if uploadID == "" {
return "", fmt.Errorf("remote upload ID required")
}
return uploadID, nil
}

func (d *Streamtape) remoteDlStatus(ctx context.Context, args model.OtherArgs) (interface{}, error) {
uploadID, err := d.extractRemoteUploadID(args)
if err != nil {
return nil, err
}

var result remoteDlStatusResult
if err := d.callAPI(ctx, "/remotedl/status", map[string]string{"id": uploadID}, &result); err != nil {
return nil, err
}
return result, nil
}

func (d *Streamtape) remoteDlRemove(ctx context.Context, args model.OtherArgs) (interface{}, error) {
uploadID, err := d.extractRemoteUploadID(args)
if err != nil {
return nil, err
}

if err := d.callAPI(ctx, "/remotedl/remove", map[string]string{"id": uploadID}, nil); err != nil {
return nil, err
}
return true, nil
}

func (d *Streamtape) fileInfo(ctx context.Context, args model.OtherArgs) (interface{}, error) {
var fileIDs string
if data, ok := args.Data.(map[string]interface{}); ok {
if ids, ok := data["file_ids"].(string); ok {
fileIDs = ids
}
}
if fileIDs == "" {
fileIDs = fileIDFromObjID(args.Obj.GetID())
}
if fileIDs == "" {
return nil, fmt.Errorf("file IDs required")
}

var result fileInfoResult
if err := d.callAPI(ctx, "/file/info", map[string]string{"file": fileIDs}, &result); err != nil {
return nil, err
}
return result, nil
}

func (d *Streamtape) thumbnail(ctx context.Context, args model.OtherArgs) (interface{}, error) {
fileID := fileIDFromObjID(args.Obj.GetID())
if fileID == "" {
return nil, fmt.Errorf("file ID required")
}

var result string
if err := d.callAPI(ctx, "/file/getsplash", map[string]string{"file": fileID}, &result); err != nil {
return nil, err
}
return result, nil
}

func (d *Streamtape) conversionStatus(ctx context.Context, args model.OtherArgs) (interface{}, error) {
isFailed := false
if data, ok := args.Data.(map[string]interface{}); ok {
if t, ok := data["type"].(string); ok && t == "failed" {
isFailed = true
}
}

endpoint := "/file/runningconverts"
if isFailed {
endpoint = "/file/failedconverts"
}

var result conversionResult
if err := d.callAPI(ctx, endpoint, nil, &result); err != nil {
return nil, err
}
return result, nil
}

var _ driver.Driver = (*Streamtape)(nil)
3 changes: 2 additions & 1 deletion drivers/streamtape/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Addition struct {
RangeConcurrency int `json:"range_concurrency" type:"number" default:"4" help:"Chunk mode concurrent upstream requests"`
RangePercent int `json:"range_percent" type:"number" default:"15" help:"Percent mode part size percentage (1-100)"`
EnableRangeControl bool `json:"enable_range_control" default:"true" help:"Enable driver-level range shaping for smoother streaming"`
Sha256 string `json:"sha256" help:"Expected SHA256 hash for upload verification (optional)"`
}

var config = driver.Config{
Expand All @@ -26,7 +27,7 @@ var config = driver.Config{
NeedMs: false,
DefaultRoot: "0",
CheckStatus: false,
Alert: "",
Alert: "warning|Moving files to root folder is not supported by Streamtape API",
NoOverwriteUpload: false,
ProxyRangeOption: true,
}
Expand Down
43 changes: 43 additions & 0 deletions drivers/streamtape/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,46 @@ type createFolderResult struct {
type uploadURLResult struct {
URL string `json:"url"`
}

type remoteDlAddResult struct {
ID string `json:"id"`
FolderID string `json:"folderid"`
}

type remoteDlStatusResult map[string]remoteDlStatusItem

type remoteDlStatusItem struct {
ID string `json:"id"`
RemoteURL string `json:"remoteurl"`
Status string `json:"status"`
BytesLoaded interface{} `json:"bytes_loaded"`
BytesTotal interface{} `json:"bytes_total"`
FolderID string `json:"folderid"`
Added string `json:"added"`
LastUpdate string `json:"last_update"`
ExtID bool `json:"extid"`
URL bool `json:"url"`
}

type fileInfoResult map[string]fileInfoItem

type fileInfoItem struct {
ID string `json:"id"`
Name string `json:"name"`
Size int64 `json:"size"`
Type string `json:"type"`
Converted bool `json:"converted"`
Status int `json:"status"`
}

type conversionResult []conversionItem

type conversionItem struct {
Name string `json:"name"`
FolderID string `json:"folderid"`
Status string `json:"status"`
Progress int `json:"progress"`
Retries int `json:"retries"`
Link string `json:"link"`
LinkID string `json:"linkid"`
}
17 changes: 17 additions & 0 deletions drivers/streamtape/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,20 @@ func extractFileIDFromUploadBody(body []byte) string {
}
return ""
}

const remoteUploadPrefix = "ru:"

func encodeRemoteUploadID(id string) string {
return remoteUploadPrefix + id
}

func remoteUploadIDFromObjID(id string) string {
if strings.HasPrefix(id, remoteUploadPrefix) {
return strings.TrimPrefix(id, remoteUploadPrefix)
}
return ""
}

func isRemoteUploadID(id string) bool {
return strings.HasPrefix(id, remoteUploadPrefix)
}
Loading