Add big file upload support
This commit is contained in:
parent
b2c4da3164
commit
b89f9b8c1a
@ -12,10 +12,9 @@ processed at a time.
|
|||||||
|
|
||||||
The bot uses the [Telegram MTProto API](https://github.com/gotd/td), which
|
The bot uses the [Telegram MTProto API](https://github.com/gotd/td), which
|
||||||
supports larger video uploads than the default 50MB with the standard
|
supports larger video uploads than the default 50MB with the standard
|
||||||
Telegram bot API. Videos are not saved on disk, they are simultaneously
|
Telegram bot API. Videos are not saved on disk. Incompatible video and audio
|
||||||
uploaded from the source to Telegram. Incompatible video and audio streams
|
streams are automatically converted to match those which are supported by
|
||||||
are automatically converted to match those which are supported by Telegram's
|
Telegram's built-in video player.
|
||||||
built-in video player.
|
|
||||||
|
|
||||||
The only dependencies are [yt-dlp](https://github.com/yt-dlp/yt-dlp) and
|
The only dependencies are [yt-dlp](https://github.com/yt-dlp/yt-dlp) and
|
||||||
[ffmpeg](https://github.com/FFmpeg/FFmpeg). Tested on Linux, but should be
|
[ffmpeg](https://github.com/FFmpeg/FFmpeg). Tested on Linux, but should be
|
||||||
|
|||||||
@ -158,11 +158,11 @@ func (c *Converter) ffmpegProgressSock() (sockFilename string, sock net.Listener
|
|||||||
if len(a) > 0 && len(a[len(a)-1]) > 0 {
|
if len(a) > 0 && len(a[len(a)-1]) > 0 {
|
||||||
data = ""
|
data = ""
|
||||||
l, _ := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
|
l, _ := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
|
||||||
c.UpdateProgressPercentCallback(int(100 * float64(l) / c.Duration / 1000000))
|
c.UpdateProgressPercentCallback(processStr, int(100*float64(l)/c.Duration/1000000))
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(data, "progress=end") {
|
if strings.Contains(data, "progress=end") {
|
||||||
c.UpdateProgressPercentCallback(100)
|
c.UpdateProgressPercentCallback(processStr, 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -217,7 +217,7 @@ func (c *Converter) ConvertIfNeeded(ctx context.Context, rr *ReReadCloser) (io.R
|
|||||||
ff = ff.GlobalArgs("-progress", "unix:"+progressSockFilename)
|
ff = ff.GlobalArgs("-progress", "unix:"+progressSockFilename)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.UpdateProgressPercentCallback(-1)
|
c.UpdateProgressPercentCallback(processStr, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
dl.go
2
dl.go
@ -13,7 +13,7 @@ const downloadAndConvertTimeout = 5 * time.Minute
|
|||||||
|
|
||||||
type ProbeStartCallbackFunc func(ctx context.Context)
|
type ProbeStartCallbackFunc func(ctx context.Context)
|
||||||
type ConvertStartCallbackFunc func(ctx context.Context, videoCodecs, audioCodecs, convertActionsNeeded string)
|
type ConvertStartCallbackFunc func(ctx context.Context, videoCodecs, audioCodecs, convertActionsNeeded string)
|
||||||
type UpdateProgressPercentCallbackFunc func(progressPercent int)
|
type UpdateProgressPercentCallbackFunc func(progressStr string, progressPercent int)
|
||||||
|
|
||||||
type Downloader struct {
|
type Downloader struct {
|
||||||
ProbeStartFunc ProbeStartCallbackFunc
|
ProbeStartFunc ProbeStartCallbackFunc
|
||||||
|
|||||||
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.20
|
|||||||
replace github.com/wader/goutubedl => github.com/nonoo/goutubedl v0.0.0-20230814114826-c1dcced79138
|
replace github.com/wader/goutubedl => github.com/nonoo/goutubedl v0.0.0-20230814114826-c1dcced79138
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/google/go-github/v53 v53.2.0
|
github.com/google/go-github/v53 v53.2.0
|
||||||
github.com/gotd/td v0.84.0
|
github.com/gotd/td v0.84.0
|
||||||
github.com/u2takey/ffmpeg-go v0.5.0
|
github.com/u2takey/ffmpeg-go v0.5.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -13,6 +13,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
|||||||
20
main.go
20
main.go
@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -22,23 +21,6 @@ var dlQueue DownloadQueue
|
|||||||
var telegramUploader *uploader.Uploader
|
var telegramUploader *uploader.Uploader
|
||||||
var telegramSender *message.Sender
|
var telegramSender *message.Sender
|
||||||
|
|
||||||
func uploadFile(ctx context.Context, entities tg.Entities, u *tg.UpdateNewMessage, f io.ReadCloser) error {
|
|
||||||
upload, err := telegramUploader.FromReader(ctx, "yt-dlp", f)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("uploading %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have uploaded file handle, sending it as styled message. First, preparing message.
|
|
||||||
document := message.UploadedDocument(upload).Video()
|
|
||||||
|
|
||||||
// Sending message with media.
|
|
||||||
if _, err := telegramSender.Answer(entities, u).Media(ctx, document); err != nil {
|
|
||||||
return fmt.Errorf("send: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleCmdDLP(ctx context.Context, entities tg.Entities, u *tg.UpdateNewMessage, msg *tg.Message) {
|
func handleCmdDLP(ctx context.Context, entities tg.Entities, u *tg.UpdateNewMessage, msg *tg.Message) {
|
||||||
// Check if message is an URL.
|
// Check if message is an URL.
|
||||||
validURI := true
|
validURI := true
|
||||||
@ -155,7 +137,7 @@ func main() {
|
|||||||
|
|
||||||
api := client.API()
|
api := client.API()
|
||||||
|
|
||||||
telegramUploader = uploader.NewUploader(api)
|
telegramUploader = uploader.NewUploader(api).WithProgress(dlUploader)
|
||||||
telegramSender = message.NewSender(api).WithUploader(telegramUploader)
|
telegramSender = message.NewSender(api).WithUploader(telegramUploader)
|
||||||
|
|
||||||
dlQueue.Init(ctx)
|
dlQueue.Init(ctx)
|
||||||
|
|||||||
169
queue.go
169
queue.go
@ -12,7 +12,8 @@ import (
|
|||||||
|
|
||||||
const processStartStr = "🔍 Getting information..."
|
const processStartStr = "🔍 Getting information..."
|
||||||
const processStr = "🔨 Processing"
|
const processStr = "🔨 Processing"
|
||||||
const processDoneStr = "🏁 Processing"
|
const uploadStr = "☁️ Uploading"
|
||||||
|
const uploadDoneStr = "🏁 Uploading"
|
||||||
const errorStr = "❌ Error"
|
const errorStr = "❌ Error"
|
||||||
const canceledStr = "❌ Canceled"
|
const canceledStr = "❌ Canceled"
|
||||||
|
|
||||||
@ -60,10 +61,26 @@ func (e *DownloadQueueEntry) editReply(ctx context.Context, s string) {
|
|||||||
e.sendTypingAction(ctx)
|
e.sendTypingAction(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type currentlyDownloadedEntryType struct {
|
||||||
|
disableProgressPercentUpdate bool
|
||||||
|
progressPercentUpdateMutex sync.Mutex
|
||||||
|
lastProgressPercentUpdateAt time.Time
|
||||||
|
lastProgressPercent int
|
||||||
|
lastDisplayedProgressPercent int
|
||||||
|
progressUpdateTimer *time.Timer
|
||||||
|
|
||||||
|
sourceCodecInfo string
|
||||||
|
progressInfo string
|
||||||
|
}
|
||||||
|
|
||||||
type DownloadQueue struct {
|
type DownloadQueue struct {
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
entries []DownloadQueueEntry
|
entries []DownloadQueueEntry
|
||||||
processReqChan chan bool
|
processReqChan chan bool
|
||||||
|
|
||||||
|
currentlyDownloadedEntry currentlyDownloadedEntryType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DownloadQueue) getQueuePositionString(pos int) string {
|
func (e *DownloadQueue) getQueuePositionString(pos int) string {
|
||||||
@ -115,17 +132,56 @@ func (q *DownloadQueue) CancelCurrentEntry(ctx context.Context, entities tg.Enti
|
|||||||
q.mutex.Unlock()
|
q.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *DownloadQueue) updateProgress(ctx context.Context, qEntry *DownloadQueueEntry, progressPercent int, sourceCodecInfo string) {
|
func (q *DownloadQueue) updateProgress(ctx context.Context, qEntry *DownloadQueueEntry, progressStr string, progressPercent int) {
|
||||||
if progressPercent < 0 {
|
if progressPercent < 0 {
|
||||||
qEntry.editReply(ctx, processStr+"... (no progress available)\n"+sourceCodecInfo)
|
qEntry.editReply(ctx, progressStr+"... (no progress available)\n"+q.currentlyDownloadedEntry.sourceCodecInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if progressPercent == 0 {
|
if progressPercent == 0 {
|
||||||
qEntry.editReply(ctx, processStr+"...\n"+sourceCodecInfo)
|
qEntry.editReply(ctx, progressStr+"..."+q.currentlyDownloadedEntry.progressInfo+"\n"+q.currentlyDownloadedEntry.sourceCodecInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Print(" progress: ", progressPercent, "%\n")
|
fmt.Print(" progress: ", progressPercent, "%\n")
|
||||||
qEntry.editReply(ctx, processStr+": "+getProgressbar(progressPercent, progressBarLength)+"\n"+sourceCodecInfo)
|
qEntry.editReply(ctx, progressStr+": "+getProgressbar(progressPercent, progressBarLength)+q.currentlyDownloadedEntry.progressInfo+"\n"+q.currentlyDownloadedEntry.sourceCodecInfo)
|
||||||
|
q.currentlyDownloadedEntry.lastDisplayedProgressPercent = progressPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *DownloadQueue) HandleProgressPercentUpdate(progressStr string, progressPercent int) {
|
||||||
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Lock()
|
||||||
|
defer q.currentlyDownloadedEntry.progressPercentUpdateMutex.Unlock()
|
||||||
|
|
||||||
|
if q.currentlyDownloadedEntry.disableProgressPercentUpdate || q.currentlyDownloadedEntry.lastProgressPercent == progressPercent {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
q.currentlyDownloadedEntry.lastProgressPercent = progressPercent
|
||||||
|
if progressPercent < 0 {
|
||||||
|
q.currentlyDownloadedEntry.disableProgressPercentUpdate = true
|
||||||
|
q.updateProgress(q.ctx, &q.entries[0], progressStr, progressPercent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.currentlyDownloadedEntry.progressUpdateTimer != nil {
|
||||||
|
q.currentlyDownloadedEntry.progressUpdateTimer.Stop()
|
||||||
|
select {
|
||||||
|
case <-q.currentlyDownloadedEntry.progressUpdateTimer.C:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timeElapsedSinceLastUpdate := time.Since(q.currentlyDownloadedEntry.lastProgressPercentUpdateAt)
|
||||||
|
if timeElapsedSinceLastUpdate < maxProgressPercentUpdateInterval {
|
||||||
|
q.currentlyDownloadedEntry.progressUpdateTimer = time.AfterFunc(maxProgressPercentUpdateInterval-timeElapsedSinceLastUpdate, func() {
|
||||||
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Lock()
|
||||||
|
if !q.currentlyDownloadedEntry.disableProgressPercentUpdate {
|
||||||
|
q.updateProgress(q.ctx, &q.entries[0], progressStr, progressPercent)
|
||||||
|
q.currentlyDownloadedEntry.lastProgressPercentUpdateAt = time.Now()
|
||||||
|
}
|
||||||
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Unlock()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
q.updateProgress(q.ctx, &q.entries[0], progressStr, progressPercent)
|
||||||
|
q.currentlyDownloadedEntry.lastProgressPercentUpdateAt = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *DownloadQueue) processQueueEntry(ctx context.Context, qEntry *DownloadQueueEntry) {
|
func (q *DownloadQueue) processQueueEntry(ctx context.Context, qEntry *DownloadQueueEntry) {
|
||||||
@ -138,109 +194,71 @@ func (q *DownloadQueue) processQueueEntry(ctx context.Context, qEntry *DownloadQ
|
|||||||
|
|
||||||
qEntry.editReply(ctx, processStartStr)
|
qEntry.editReply(ctx, processStartStr)
|
||||||
|
|
||||||
var disableProgressPercentUpdate bool
|
|
||||||
var progressPercentUpdateMutex sync.Mutex
|
|
||||||
var lastProgressPercentUpdateAt time.Time
|
|
||||||
var lastProgressPercent int
|
|
||||||
var progressUpdateTimer *time.Timer
|
|
||||||
var sourceCodecInfo string
|
|
||||||
downloader := Downloader{
|
downloader := Downloader{
|
||||||
ProbeStartFunc: func(ctx context.Context) {
|
ProbeStartFunc: func(ctx context.Context) {
|
||||||
qEntry.editReply(ctx, "🎬 Getting video format...")
|
qEntry.editReply(ctx, "🎬 Getting video format...")
|
||||||
},
|
},
|
||||||
ConvertStartFunc: func(ctx context.Context, videoCodecs, audioCodecs, convertActionsNeeded string) {
|
ConvertStartFunc: func(ctx context.Context, videoCodecs, audioCodecs, convertActionsNeeded string) {
|
||||||
sourceCodecInfo = "🎬 Source: " + videoCodecs
|
q.currentlyDownloadedEntry.sourceCodecInfo = "🎬 Source: " + videoCodecs
|
||||||
if audioCodecs == "" {
|
if audioCodecs == "" {
|
||||||
sourceCodecInfo += ", no audio"
|
q.currentlyDownloadedEntry.sourceCodecInfo += ", no audio"
|
||||||
} else {
|
} else {
|
||||||
sourceCodecInfo += " / " + audioCodecs
|
q.currentlyDownloadedEntry.sourceCodecInfo += " / " + audioCodecs
|
||||||
}
|
}
|
||||||
if convertActionsNeeded == "" {
|
if convertActionsNeeded == "" {
|
||||||
sourceCodecInfo += " (no conversion needed)"
|
q.currentlyDownloadedEntry.sourceCodecInfo += " (no conversion needed)"
|
||||||
} else {
|
} else {
|
||||||
sourceCodecInfo += " (converting: " + convertActionsNeeded + ")"
|
q.currentlyDownloadedEntry.sourceCodecInfo += " (converting: " + convertActionsNeeded + ")"
|
||||||
}
|
}
|
||||||
qEntry.editReply(ctx, "🎬 Preparing download...\n"+sourceCodecInfo)
|
qEntry.editReply(ctx, "🎬 Preparing download...\n"+q.currentlyDownloadedEntry.sourceCodecInfo)
|
||||||
},
|
|
||||||
UpdateProgressPercentFunc: func(progressPercent int) {
|
|
||||||
progressPercentUpdateMutex.Lock()
|
|
||||||
defer progressPercentUpdateMutex.Unlock()
|
|
||||||
|
|
||||||
if disableProgressPercentUpdate || lastProgressPercent == progressPercent {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lastProgressPercent = progressPercent
|
|
||||||
if progressPercent < 0 {
|
|
||||||
disableProgressPercentUpdate = true
|
|
||||||
q.updateProgress(ctx, qEntry, progressPercent, sourceCodecInfo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if progressUpdateTimer != nil {
|
|
||||||
progressUpdateTimer.Stop()
|
|
||||||
select {
|
|
||||||
case <-progressUpdateTimer.C:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timeElapsedSinceLastUpdate := time.Since(lastProgressPercentUpdateAt)
|
|
||||||
if timeElapsedSinceLastUpdate < maxProgressPercentUpdateInterval {
|
|
||||||
progressUpdateTimer = time.AfterFunc(maxProgressPercentUpdateInterval-timeElapsedSinceLastUpdate, func() {
|
|
||||||
q.updateProgress(ctx, qEntry, progressPercent, sourceCodecInfo)
|
|
||||||
lastProgressPercentUpdateAt = time.Now()
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
q.updateProgress(ctx, qEntry, progressPercent, sourceCodecInfo)
|
|
||||||
lastProgressPercentUpdateAt = time.Now()
|
|
||||||
},
|
},
|
||||||
|
UpdateProgressPercentFunc: q.HandleProgressPercentUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := downloader.DownloadAndConvertURL(qEntry.Ctx, qEntry.OrigMsg.Message)
|
r, err := downloader.DownloadAndConvertURL(qEntry.Ctx, qEntry.OrigMsg.Message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(" error downloading:", err)
|
fmt.Println(" error downloading:", err)
|
||||||
progressPercentUpdateMutex.Lock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Lock()
|
||||||
disableProgressPercentUpdate = true
|
q.currentlyDownloadedEntry.disableProgressPercentUpdate = true
|
||||||
progressPercentUpdateMutex.Unlock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Unlock()
|
||||||
qEntry.editReply(ctx, fmt.Sprint(errorStr+": ", err))
|
qEntry.editReply(ctx, fmt.Sprint(errorStr+": ", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Feeding the returned io.ReadCloser to the uploader.
|
// Feeding the returned io.ReadCloser to the uploader.
|
||||||
fmt.Println(" processing...")
|
fmt.Println(" processing...")
|
||||||
progressPercentUpdateMutex.Lock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Lock()
|
||||||
q.updateProgress(ctx, qEntry, lastProgressPercent, sourceCodecInfo)
|
q.updateProgress(ctx, qEntry, processStr, q.currentlyDownloadedEntry.lastProgressPercent)
|
||||||
progressPercentUpdateMutex.Unlock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Unlock()
|
||||||
|
|
||||||
err = uploadFile(ctx, qEntry.OrigEntities, qEntry.OrigMsgUpdate, r)
|
err = dlUploader.UploadFile(qEntry.Ctx, qEntry.OrigEntities, qEntry.OrigMsgUpdate, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(" error processing:", err)
|
fmt.Println(" error processing:", err)
|
||||||
progressPercentUpdateMutex.Lock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Lock()
|
||||||
disableProgressPercentUpdate = true
|
q.currentlyDownloadedEntry.disableProgressPercentUpdate = true
|
||||||
progressPercentUpdateMutex.Unlock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Unlock()
|
||||||
r.Close()
|
r.Close()
|
||||||
qEntry.editReply(ctx, fmt.Sprint(errorStr+": ", err))
|
qEntry.editReply(ctx, fmt.Sprint(errorStr+": ", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
progressPercentUpdateMutex.Lock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Lock()
|
||||||
disableProgressPercentUpdate = true
|
q.currentlyDownloadedEntry.disableProgressPercentUpdate = true
|
||||||
progressPercentUpdateMutex.Unlock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Unlock()
|
||||||
r.Close()
|
r.Close()
|
||||||
|
|
||||||
progressPercentUpdateMutex.Lock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Lock()
|
||||||
if qEntry.Canceled {
|
if qEntry.Canceled {
|
||||||
fmt.Print(" canceled\n")
|
fmt.Print(" canceled\n")
|
||||||
qEntry.editReply(ctx, canceledStr+": "+getProgressbar(lastProgressPercent, progressBarLength)+"\n"+sourceCodecInfo)
|
q.updateProgress(ctx, qEntry, canceledStr, q.currentlyDownloadedEntry.lastProgressPercent)
|
||||||
} else if lastProgressPercent < 100 {
|
} else if q.currentlyDownloadedEntry.lastDisplayedProgressPercent < 100 {
|
||||||
fmt.Print(" progress: 100%\n")
|
fmt.Print(" progress: 100%\n")
|
||||||
qEntry.editReply(ctx, processDoneStr+": "+getProgressbar(100, progressBarLength)+"\n"+sourceCodecInfo)
|
q.updateProgress(ctx, qEntry, uploadDoneStr, 100)
|
||||||
}
|
}
|
||||||
progressPercentUpdateMutex.Unlock()
|
q.currentlyDownloadedEntry.progressPercentUpdateMutex.Unlock()
|
||||||
qEntry.sendTypingCancelAction(ctx)
|
qEntry.sendTypingCancelAction(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *DownloadQueue) processor(ctx context.Context) {
|
func (q *DownloadQueue) processor() {
|
||||||
for {
|
for {
|
||||||
q.mutex.Lock()
|
q.mutex.Lock()
|
||||||
if (len(q.entries)) == 0 {
|
if (len(q.entries)) == 0 {
|
||||||
@ -251,16 +269,18 @@ func (q *DownloadQueue) processor(ctx context.Context) {
|
|||||||
|
|
||||||
// Updating queue positions for all waiting entries.
|
// Updating queue positions for all waiting entries.
|
||||||
for i := 1; i < len(q.entries); i++ {
|
for i := 1; i < len(q.entries); i++ {
|
||||||
q.entries[i].editReply(ctx, q.getQueuePositionString(i))
|
q.entries[i].editReply(q.ctx, q.getQueuePositionString(i))
|
||||||
q.entries[i].sendTypingCancelAction(ctx)
|
q.entries[i].sendTypingCancelAction(q.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
q.entries[0].Ctx, q.entries[0].CtxCancel = context.WithTimeout(ctx, downloadAndConvertTimeout)
|
q.entries[0].Ctx, q.entries[0].CtxCancel = context.WithTimeout(q.ctx, downloadAndConvertTimeout)
|
||||||
|
|
||||||
qEntry := &q.entries[0]
|
qEntry := &q.entries[0]
|
||||||
q.mutex.Unlock()
|
q.mutex.Unlock()
|
||||||
|
|
||||||
q.processQueueEntry(ctx, qEntry)
|
q.currentlyDownloadedEntry = currentlyDownloadedEntryType{}
|
||||||
|
|
||||||
|
q.processQueueEntry(q.ctx, qEntry)
|
||||||
|
|
||||||
q.mutex.Lock()
|
q.mutex.Lock()
|
||||||
q.entries[0].CtxCancel()
|
q.entries[0].CtxCancel()
|
||||||
@ -273,6 +293,7 @@ func (q *DownloadQueue) processor(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *DownloadQueue) Init(ctx context.Context) {
|
func (q *DownloadQueue) Init(ctx context.Context) {
|
||||||
|
q.ctx = ctx
|
||||||
q.processReqChan = make(chan bool)
|
q.processReqChan = make(chan bool)
|
||||||
go q.processor(ctx)
|
go q.processor()
|
||||||
}
|
}
|
||||||
|
|||||||
57
upload.go
Normal file
57
upload.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
"github.com/gotd/td/telegram/message"
|
||||||
|
"github.com/gotd/td/telegram/uploader"
|
||||||
|
"github.com/gotd/td/tg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Uploader struct{}
|
||||||
|
|
||||||
|
var dlUploader Uploader
|
||||||
|
|
||||||
|
func (p Uploader) Chunk(ctx context.Context, state uploader.ProgressState) error {
|
||||||
|
dlQueue.HandleProgressPercentUpdate(uploadStr, int(state.Uploaded*100/state.Total))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Uploader) UploadFile(ctx context.Context, entities tg.Entities, u *tg.UpdateNewMessage, f io.ReadCloser) error {
|
||||||
|
// Reading to a buffer first, because we don't know the file size.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for {
|
||||||
|
b := make([]byte, 1024)
|
||||||
|
n, err := f.Read(b)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return fmt.Errorf("reading to buffer error: %w", err)
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf.Write(b[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(" got", buf.Len(), "bytes, uploading...")
|
||||||
|
dlQueue.currentlyDownloadedEntry.progressInfo = fmt.Sprint(" (", humanize.BigBytes(big.NewInt(int64(buf.Len()))), ")")
|
||||||
|
|
||||||
|
upload, err := telegramUploader.FromBytes(ctx, "yt-dlp", buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("uploading %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we have uploaded file handle, sending it as styled message. First, preparing message.
|
||||||
|
document := message.UploadedDocument(upload).Video()
|
||||||
|
|
||||||
|
// Sending message with media.
|
||||||
|
if _, err := telegramSender.Answer(entities, u).Media(ctx, document); err != nil {
|
||||||
|
return fmt.Errorf("send: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user