Fix duplicate links for files
This commit is contained in:
@@ -27,10 +27,6 @@ const (
|
|||||||
WebDavUseOriginalNameNoExt WebDavFolderNaming = "original_no_ext"
|
WebDavUseOriginalNameNoExt WebDavFolderNaming = "original_no_ext"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DownloadLinkCache struct {
|
|
||||||
Link string `json:"download_link"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PropfindResponse struct {
|
type PropfindResponse struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
GzippedData []byte
|
GzippedData []byte
|
||||||
@@ -112,8 +108,6 @@ func (c *Cache) setTorrent(t *CachedTorrent) {
|
|||||||
c.torrentsNames[c.GetTorrentFolder(t.Torrent)] = t
|
c.torrentsNames[c.GetTorrentFolder(t.Torrent)] = t
|
||||||
c.torrentsMutex.Unlock()
|
c.torrentsMutex.Unlock()
|
||||||
|
|
||||||
c.refreshListings()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := c.SaveTorrent(t); err != nil {
|
if err := c.SaveTorrent(t); err != nil {
|
||||||
c.logger.Debug().Err(err).Msgf("Failed to save torrent %s", t.Id)
|
c.logger.Debug().Err(err).Msgf("Failed to save torrent %s", t.Id)
|
||||||
@@ -224,6 +218,19 @@ func (c *Cache) load() (map[string]*CachedTorrent, error) {
|
|||||||
}
|
}
|
||||||
if len(ct.Files) != 0 {
|
if len(ct.Files) != 0 {
|
||||||
// We can assume the torrent is complete
|
// We can assume the torrent is complete
|
||||||
|
|
||||||
|
// Make sure no file has a duplicate link
|
||||||
|
linkStore := make(map[string]bool)
|
||||||
|
for _, f := range ct.Files {
|
||||||
|
if _, ok := linkStore[f.Link]; ok {
|
||||||
|
// Duplicate link, refresh the torrent
|
||||||
|
ct = *c.refreshTorrent(&ct)
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
linkStore[f.Link] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ct.IsComplete = true
|
ct.IsComplete = true
|
||||||
torrents[ct.Id] = &ct
|
torrents[ct.Id] = &ct
|
||||||
}
|
}
|
||||||
@@ -369,7 +376,7 @@ func (c *Cache) sync(torrents []*types.Torrent) error {
|
|||||||
return // Channel closed, exit goroutine
|
return // Channel closed, exit goroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ProcessTorrent(t, true); err != nil {
|
if err := c.ProcessTorrent(t, false); err != nil {
|
||||||
c.logger.Error().Err(err).Str("torrent", t.Name).Msg("sync error")
|
c.logger.Error().Err(err).Str("torrent", t.Name).Msg("sync error")
|
||||||
atomic.AddInt64(&errorCount, 1)
|
atomic.AddInt64(&errorCount, 1)
|
||||||
}
|
}
|
||||||
@@ -402,6 +409,7 @@ func (c *Cache) sync(torrents []*types.Torrent) error {
|
|||||||
// Wait for all workers to complete
|
// Wait for all workers to complete
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
c.refreshListings()
|
||||||
c.logger.Info().Msgf("Sync complete: %d torrents processed, %d errors", len(torrents), errorCount)
|
c.logger.Info().Msgf("Sync complete: %d torrents processed, %d errors", len(torrents), errorCount)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -412,13 +420,16 @@ func (c *Cache) ProcessTorrent(t *types.Torrent, refreshRclone bool) error {
|
|||||||
return fmt.Errorf("failed to update torrent: %w", err)
|
return fmt.Errorf("failed to update torrent: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ct := &CachedTorrent{
|
ct := &CachedTorrent{
|
||||||
Torrent: t,
|
Torrent: t,
|
||||||
LastRead: time.Now(),
|
LastRead: time.Now(),
|
||||||
IsComplete: len(t.Files) > 0,
|
IsComplete: len(t.Files) > 0,
|
||||||
}
|
}
|
||||||
c.setTorrent(ct)
|
c.setTorrent(ct)
|
||||||
|
|
||||||
|
if refreshRclone {
|
||||||
|
c.refreshListings()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +458,6 @@ func (c *Cache) GetDownloadLink(torrentId, filename, fileLink string) string {
|
|||||||
file = ct.Files[filename]
|
file = ct.Files[filename]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Trace().Msgf("Getting download link for %s", ct.Name)
|
c.logger.Trace().Msgf("Getting download link for %s", ct.Name)
|
||||||
link, err := c.client.GetDownloadLink(ct.Torrent, &file)
|
link, err := c.client.GetDownloadLink(ct.Torrent, &file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -463,6 +473,39 @@ func (c *Cache) GetDownloadLink(torrentId, filename, fileLink string) string {
|
|||||||
return file.DownloadLink
|
return file.DownloadLink
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cache) GenerateDownloadLinks(t *CachedTorrent) {
|
||||||
|
if err := c.client.GenerateDownloadLinks(t.Torrent); err != nil {
|
||||||
|
c.logger.Error().Err(err).Msg("Failed to generate download links")
|
||||||
|
}
|
||||||
|
for _, file := range t.Files {
|
||||||
|
c.updateDownloadLink(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := c.SaveTorrent(t); err != nil {
|
||||||
|
c.logger.Debug().Err(err).Msgf("Failed to save torrent %s", t.Id)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) AddTorrent(t *types.Torrent) error {
|
||||||
|
if len(t.Files) == 0 {
|
||||||
|
if err := c.client.UpdateTorrent(t); err != nil {
|
||||||
|
return fmt.Errorf("failed to update torrent: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ct := &CachedTorrent{
|
||||||
|
Torrent: t,
|
||||||
|
LastRead: time.Now(),
|
||||||
|
IsComplete: len(t.Files) > 0,
|
||||||
|
}
|
||||||
|
c.setTorrent(ct)
|
||||||
|
c.refreshListings()
|
||||||
|
go c.GenerateDownloadLinks(ct)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) updateDownloadLink(file types.File) {
|
func (c *Cache) updateDownloadLink(file types.File) {
|
||||||
c.downloadLinksMutex.Lock()
|
c.downloadLinksMutex.Lock()
|
||||||
defer c.downloadLinksMutex.Unlock()
|
defer c.downloadLinksMutex.Unlock()
|
||||||
@@ -489,6 +532,8 @@ func (c *Cache) DeleteTorrent(id string) {
|
|||||||
delete(c.torrentsNames, t.Name)
|
delete(c.torrentsNames, t.Name)
|
||||||
|
|
||||||
c.removeFromDB(id)
|
c.removeFromDB(id)
|
||||||
|
|
||||||
|
c.refreshListings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,9 +545,10 @@ func (c *Cache) DeleteTorrents(ids []string) {
|
|||||||
if t, ok := c.torrents[id]; ok {
|
if t, ok := c.torrents[id]; ok {
|
||||||
delete(c.torrents, id)
|
delete(c.torrents, id)
|
||||||
delete(c.torrentsNames, c.GetTorrentFolder(t.Torrent))
|
delete(c.torrentsNames, c.GetTorrentFolder(t.Torrent))
|
||||||
c.removeFromDB(id)
|
go c.removeFromDB(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.refreshListings()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) removeFromDB(torrentId string) {
|
func (c *Cache) removeFromDB(torrentId string) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func (c *Cache) RefreshXml() error {
|
|||||||
return fmt.Errorf("failed to refresh XML for %s: %v", parent, err)
|
return fmt.Errorf("failed to refresh XML for %s: %v", parent, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.logger.Trace().Msgf("Refreshed XML cache for %s", c.client.GetName())
|
c.logger.Debug().Msgf("Refreshed XML cache for %s", c.client.GetName())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,12 +74,6 @@ func getTorrentFiles(t *types.Torrent, data TorrentInfo, validate bool) map[stri
|
|||||||
_link = data.Links[idx]
|
_link = data.Links[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
if a, ok := t.Files[name]; ok {
|
|
||||||
a.Link = _link
|
|
||||||
files[name] = a
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Name: name,
|
Name: name,
|
||||||
Path: name,
|
Path: name,
|
||||||
@@ -268,17 +262,15 @@ func (r *RealDebrid) DeleteTorrent(torrentId string) {
|
|||||||
|
|
||||||
func (r *RealDebrid) GenerateDownloadLinks(t *types.Torrent) error {
|
func (r *RealDebrid) GenerateDownloadLinks(t *types.Torrent) error {
|
||||||
url := fmt.Sprintf("%s/unrestrict/link/", r.Host)
|
url := fmt.Sprintf("%s/unrestrict/link/", r.Host)
|
||||||
|
files := make(map[string]types.File)
|
||||||
for _, f := range t.Files {
|
for _, f := range t.Files {
|
||||||
if f.DownloadLink != "" {
|
|
||||||
// Or check the generated link
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
payload := gourl.Values{
|
payload := gourl.Values{
|
||||||
"link": {f.Link},
|
"link": {f.Link},
|
||||||
}
|
}
|
||||||
req, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(payload.Encode()))
|
req, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(payload.Encode()))
|
||||||
resp, err := r.client.MakeRequest(req)
|
resp, err := r.client.MakeRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var data UnrestrictResponse
|
var data UnrestrictResponse
|
||||||
@@ -287,8 +279,9 @@ func (r *RealDebrid) GenerateDownloadLinks(t *types.Torrent) error {
|
|||||||
}
|
}
|
||||||
f.DownloadLink = data.Download
|
f.DownloadLink = data.Download
|
||||||
f.Generated = time.Now()
|
f.Generated = time.Now()
|
||||||
t.Files[f.Name] = f
|
files[f.Name] = f
|
||||||
}
|
}
|
||||||
|
t.Files = files
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func (q *QBit) ProcessFiles(torrent *Torrent, debridTorrent *debrid.Torrent, arr
|
|||||||
if ok {
|
if ok {
|
||||||
q.logger.Info().Msgf("Using internal webdav for %s", debridTorrent.Debrid)
|
q.logger.Info().Msgf("Using internal webdav for %s", debridTorrent.Debrid)
|
||||||
// Use webdav to download the file
|
// Use webdav to download the file
|
||||||
err := cache.ProcessTorrent(debridTorrent, true)
|
err := cache.AddTorrent(debridTorrent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ import (
|
|||||||
|
|
||||||
type WebDav struct {
|
type WebDav struct {
|
||||||
Handlers []*Handler
|
Handlers []*Handler
|
||||||
|
ready chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *WebDav {
|
func New() *WebDav {
|
||||||
svc := service.GetService()
|
svc := service.GetService()
|
||||||
w := &WebDav{
|
w := &WebDav{
|
||||||
Handlers: make([]*Handler, 0),
|
Handlers: make([]*Handler, 0),
|
||||||
|
ready: make(chan struct{}),
|
||||||
}
|
}
|
||||||
for name, c := range svc.Debrid.Caches {
|
for name, c := range svc.Debrid.Caches {
|
||||||
h := NewHandler(name, c, logger.NewLogger(fmt.Sprintf("%s-webdav", name)))
|
h := NewHandler(name, c, logger.NewLogger(fmt.Sprintf("%s-webdav", name)))
|
||||||
@@ -38,6 +40,22 @@ func (wd *WebDav) Routes() http.Handler {
|
|||||||
wr := chi.NewRouter()
|
wr := chi.NewRouter()
|
||||||
wr.Use(wd.commonMiddleware)
|
wr.Use(wd.commonMiddleware)
|
||||||
|
|
||||||
|
// Create a readiness check middleware
|
||||||
|
readinessMiddleware := func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
select {
|
||||||
|
case <-wd.ready:
|
||||||
|
// WebDAV is ready, proceed
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
default:
|
||||||
|
// WebDAV is still initializing
|
||||||
|
w.Header().Set("Retry-After", "10")
|
||||||
|
http.Error(w, "WebDAV service is initializing, please try again shortly", http.StatusServiceUnavailable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
wr.Use(readinessMiddleware)
|
||||||
|
|
||||||
wd.setupRootHandler(wr)
|
wd.setupRootHandler(wr)
|
||||||
wd.mountHandlers(wr)
|
wd.mountHandlers(wr)
|
||||||
|
|
||||||
@@ -65,6 +83,9 @@ func (wd *WebDav) Start(ctx context.Context) error {
|
|||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(errChan)
|
close(errChan)
|
||||||
|
|
||||||
|
// Signal that WebDAV is ready
|
||||||
|
close(wd.ready)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Collect all errors
|
// Collect all errors
|
||||||
|
|||||||
Reference in New Issue
Block a user