Skip to content

Commit

Permalink
Fix #55 Add --show-path option
Browse files Browse the repository at this point in the history
  • Loading branch information
simulot committed Nov 15, 2020
1 parent 095b268 commit 6e9a153
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 97 deletions.
39 changes: 29 additions & 10 deletions README.md
Expand Up @@ -59,26 +59,25 @@ tar -czvf aspiratv_0.8.0_Linux_x86_64.tar.gz
```
Usage of aspiratv:
--config string Configuration file name. (default "config.json")
-d, --destination string Destination path.
-d, --destination string Destination path for all shows.
--force Force media download.
--headless Headless mode. Progression bars are not displayed.
-b, --keep-bonuses Download bonuses when true (default true)
--log string Give the log file name.
-l, --log-level string Log level (INFO,TRACE,ERROR,DEBUG) (default "ERROR")
-a, --max-aged int Retrieve media younger than MaxAgedDays.
-m, --max-tasks int Maximum concurrent downloads at a time. (default 8)
-p, --provider string Provider to be used with download command. Possible values : artetv,francetv,gulli
-p, --provider string Provider to be used with download command. Possible values : artetv, francetv, gulli
-s, --show-path string Force show's path.
-e, --title-exclude string Showtitle and Episode title must not satisfy regexp filter
-f, --title-filter string Showtitle or Episode title must satisfy regexp filter
-n, --write-nfo Write NFO file for KODI,Emby,Plex... (default true)
```

Le programme fonctionne selon deux modilités :
## Pour surveiller la mise à disposition de nouveaux épisodes d'une émission
Dans ce mode, le fichiers de configuration `config.json` placé dans le même répertoire que le programe est lu pour pour interroger les différents serveur.

### --headless
L'option `--headless` désactive les barres de progressions et produit une log sur la console.
Note: L'option -server a été supprimée. Pour interroger automatiquement les serveur, ajouter une ligne dans crontab, ou une tâche planifiée dans windows.

### --config votreconfig.json
Expand All @@ -91,26 +90,46 @@ L'option `--config` indique le fichier de configuration à utiliser.
```
Cette commande cherchera les épisodes de la série "Les Dalton" sur france télévisions, et les téléchargera dans le répertoire ~/Video/DL

Utiliser l'option `--title-filter` pour télécharger un épisode précis. Le filtre est une expression régulière GO. Voir la syntaxe précise (https://golang.org/pkg/regexp/syntax/).
### --title-filter
Utiliser l'option `--title-filter` pour télécharger un épisode précis. Le filtre est une expression régulière GO. Voir la syntaxe précise (https://golang.org/pkg/regexp/syntax/). Le site (https://regex101.com/) permet de construire et tester les expressons régulières.


Par exemple, pour télécharger les émissions spéciales de "La maison France 5"

```sh
./aspiratv --provider=francetv --destination=$HOME/Videos/DL --title-filter "spéciale" download "La maison France 5"
```

Utiliser l'option `--title-exclude` pour exclure du téléchargment certains épisodes. Le filtre est une expression régulière GO. Voir la syntaxe précise (https://golang.org/pkg/regexp/syntax/).

### --title-exclude
Utiliser l'option `--title-exclude` pour exclure du téléchargment certains épisodes. Le filtre est une expression régulière GO.


Pour télécharger tous les épisodes de "La Maison France 5" sauf les émissions sépciale
```sh
./aspiratv --provider=francetv --destination=$HOME/Videos/DL --title-exclude "spéciale" download "La maison France 5"

```
La combinaison des deux filtres est possible.

### --destination
Cette option indique le répertoire de base des téléachargements.
```sh
./aspiratv --provider=francetv --destination ~/Videos/FranceTV download "C à vous" "C dans l'air"
```
### --show-path
Cette option force le chemin dans lequel la saison ou le film sera téléchargé.

Exemple, les épisodes seront téléchagés dans le répertoire `~/Videos/MaisonF5/Season ...`
```sh
./aspiratv --provider=francetv --show-path = ~/Videos/MaisonF5 download "La maison France 5"
```


## Les options communes aux deux modes :

### --headless

L option `--headless` désactive les barres de progressions et produit une log sur la console.

### --max-tasks NUM
Précise le nombre maximal de téléchargements simultanés possible. La valeur par défaut est le nombre de processeurs de la machine.

Expand Down
11 changes: 8 additions & 3 deletions cmd/aspiratv/config.go
Expand Up @@ -6,10 +6,12 @@ import (
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"

"github.com/simulot/aspiratv/providers"
"github.com/simulot/aspiratv/providers/matcher"
)

Expand Down Expand Up @@ -122,11 +124,14 @@ func (c *config) Check() {
m.Pitch = strings.ToLower(m.Pitch)
m.Show = strings.ToLower(m.Show)
m.Title = strings.ToLower(m.Title)
if _, ok := c.Destinations[m.Destination]; !ok {
log.Fatalf("Destination %q for show %q is not defined into section Destination of %q", m.Destination, m.Show, c.ConfigFile)
if len(m.ShowRootPath) == 0 {
if s, ok := c.Destinations[m.Destination]; !ok {
log.Fatalf("Destination %q for show %q is not defined into section Destination of %q", m.Destination, m.Show, c.ConfigFile)
} else {
m.ShowRootPath = filepath.Join(s, providers.PathNameCleaner(m.Show))
}
}
}

}

func (c *config) IsProviderActive(p string) bool {
Expand Down
4 changes: 1 addition & 3 deletions cmd/aspiratv/config.json
Expand Up @@ -21,9 +21,7 @@
{
"Show": "La maison France 5",
"Provider": "francetv",
"Destination": "Séries",
"TitleFilter": "(?i)spéciale",
"TitleExclude": "(?i)salon|suite|SALLE"
"Destination": "Séries"
}
]
}
21 changes: 13 additions & 8 deletions cmd/aspiratv/download.go
Expand Up @@ -76,24 +76,28 @@ func (a *app) DownloadShow(ctx context.Context, p providers.Provider, m *provide
cancel()
}()
id := 1000 + atomic.AddInt32(&dlID, 1)
itemName = filepath.Base(m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination]))
ShowPath := m.Match.ShowRootPath
if len(ShowPath) == 0 {
ShowPath = a.Config.Destinations[m.Match.Destination]
}
itemName = filepath.Base(m.Metadata.GetMediaPath(ShowPath))

err := p.GetMediaDetails(ctx, m) // Side effect: Episode number can be determined at this point.
url := m.Metadata.GetMediaInfo().URL
if err != nil || len(url) == 0 {
a.logger.Error().Printf("[%s] Can't get url from %s.", p.Name(), filepath.Base(m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination])))
a.logger.Error().Printf("[%s] Can't get url from %s.", p.Name(), filepath.Base(m.Metadata.GetMediaPath(ShowPath)))
return
}

if a.Config.WriteNFO {
a.DownloadInfo(ctx, p, a.Config.Destinations[m.Match.Destination], m, pc, id, &files)
a.DownloadInfo(ctx, p, ShowPath, m, pc, id, &files)
if ctx.Err() != nil {
return
}
}

var pgr *progressBar
fn := m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination])
fn := m.Metadata.GetMediaPath(ShowPath)
itemName = filepath.Base(fn)

a.logger.Trace().Printf("[%s] Start downloading media %q", p.Name(), fn)
Expand Down Expand Up @@ -132,7 +136,7 @@ func (a *app) DownloadShow(ctx context.Context, p providers.Provider, m *provide
func (a *app) DownloadInfo(ctx context.Context, p providers.Provider, destination string, m *providers.Media, pc *mpb.Progress, id int32, downloadedFiles *[]string) {

var metaBar *mpb.Bar
itemName := filepath.Base(m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination]))
itemName := filepath.Base(m.Metadata.GetMediaPath(destination))

defer func() {
if metaBar != nil {
Expand All @@ -154,7 +158,7 @@ func (a *app) DownloadInfo(ctx context.Context, p providers.Provider, destinatio
}

info := m.Metadata.GetMediaInfo()
nfoPath := m.Metadata.GetNFOPath(a.Config.Destinations[m.Match.Destination])
nfoPath := m.Metadata.GetNFOPath(destination)
nfoExists, err := fileExists(nfoPath)
if !nfoExists && err == nil {
err = m.Metadata.WriteNFO(nfoPath)
Expand All @@ -165,8 +169,9 @@ func (a *app) DownloadInfo(ctx context.Context, p providers.Provider, destinatio
a.DowloadImages(ctx, p, nfoPath, info.Thumb, downloadedFiles)
}
if m.ShowType == providers.Series {
seasonPath := filepath.Dir(m.Metadata.GetMediaPath(destination))
if info.SeasonInfo != nil {
nfoPath = m.Metadata.GetSeasonNFOPath(a.Config.Destinations[m.Match.Destination])
nfoPath = filepath.Join(seasonPath, "season.nfo")
nfoExists, err = fileExists(nfoPath)
if !nfoExists && err == nil {
info.SeasonInfo.WriteNFO(nfoPath)
Expand All @@ -179,7 +184,7 @@ func (a *app) DownloadInfo(ctx context.Context, p providers.Provider, destinatio
}
}
if info.TVShow != nil {
nfoPath = m.Metadata.GetShowNFOPath(a.Config.Destinations[m.Match.Destination])
nfoPath = filepath.Join(destination, "tvshow.nfo")
nfoExists, err = fileExists(nfoPath)
if !nfoExists && err == nil {
info.TVShow.WriteNFO(nfoPath)
Expand Down
72 changes: 46 additions & 26 deletions cmd/aspiratv/main.go
Expand Up @@ -47,6 +47,7 @@ type config struct {
ConcurrentTasks int // Number of concurrent downloads
Provider string // Provider for dowload command
Destination string // Destination folder for dowload command
ShowPath string // Imposed show's path
LogFile string // Log file
WriteNFO bool // True when NFO files to be written
MaxAgedDays int // Retrieve media younger than MaxAgedDays when non zero
Expand Down Expand Up @@ -93,8 +94,9 @@ func main() {
flag.BoolVar(&a.Config.Headless, "headless", false, "Headless mode. Progression bars are not displayed.")
flag.StringVar(&a.Config.ConfigFile, "config", "config.json", "Configuration file name.")
flag.IntVarP(&a.Config.ConcurrentTasks, "max-tasks", "m", runtime.NumCPU(), "Maximum concurrent downloads at a time.")
flag.StringVarP(&a.Config.Provider, "provider", "p", "", "Provider to be used with download command. Possible values : artetv,francetv,gulli")
flag.StringVarP(&a.Config.Destination, "destination", "d", "", "Destination path.")
flag.StringVarP(&a.Config.Provider, "provider", "p", "", "Provider to be used with download command. Possible values : artetv, francetv, gulli")
flag.StringVarP(&a.Config.Destination, "destination", "d", "", "Destination path for all shows.")
flag.StringVarP(&a.Config.ShowPath, "show-path", "s", "", "Force show's path.")
flag.StringVar(&a.Config.LogFile, "log", "", "Give the log file name.")
// flag.IntVar(&a.Config.RetentionDays, "retention", 0, "Delete media older than retention days for the downloaded show.")
flag.BoolVarP(&a.Config.WriteNFO, "write-nfo", "n", true, "Write NFO file for KODI,Emby,Plex...")
Expand All @@ -104,20 +106,6 @@ func main() {
flag.StringVarP(&a.Config.TitleExclude, "title-exclude", "e", "", "Showtitle and Episode title must not satisfy regexp filter")
flag.Parse()

if a.Config.TitleFilter != "" {
if _, err := regexp.Compile(a.Config.TitleFilter); err != nil {
log.Printf("Can't use the title-filter: %q", err)
os.Exit(1)
}
}

if a.Config.TitleExclude != "" {
if _, err := regexp.Compile(a.Config.TitleExclude); err != nil {
log.Printf("Can't use the title-exclude: %q", err)
os.Exit(1)
}
}

consoleLogger := log.New(os.Stderr, "", log.LstdFlags)
fileLogger := logger(nil)

Expand Down Expand Up @@ -192,35 +180,65 @@ func (a *app) CheckPaths() {

a.Config.Destinations[k] = v
}
if len(a.Config.ShowPath) > 0 {
var err error
a.Config.ShowPath, err = sanitizePath(a.Config.ShowPath)
if err != nil {
a.logger.Fatal().Printf("Can't sanitize path %q", a.Config.ShowPath)
os.Exit(1)
}
a.logger.Trace().Printf("ShowPath set to %q", a.Config.ShowPath)
}
}

func sanitizePath(p string) (string, error) {
return filepath.Abs(p)
}

// Download command
func (a *app) Download(ctx context.Context) {

if len(a.Config.Destination) == 0 {
a.logger.Fatal().Printf("--destination parameter is mandatory for download operation")
}
if len(flag.Args()) < 2 {
flag.Usage()
os.Exit(1)
}

if len(a.Config.Destination) == 0 && len(a.Config.ShowPath) == 0 {
a.logger.Fatal().Printf("Either --destination or --show-path parameter is mandatory for download operation")
}

// if (len(a.Config.Destinations) > 0) != (len(a.Config.ShowPath) > 0) {
// a.logger.Fatal().Printf("Choose one of paramters --destination or --show-path for download operation")
// }

if a.Config.TitleFilter != "" {
if _, err := regexp.Compile(a.Config.TitleFilter); err != nil {
a.logger.Fatal().Printf("Can't use the title-filter: %q", err)
}
}

if a.Config.TitleExclude != "" {
if _, err := regexp.Compile(a.Config.TitleExclude); err != nil {
a.logger.Fatal().Printf("Can't use the title-exclude: %q", err)
}
}

a.Config.Destinations = map[string]string{
"DL": os.ExpandEnv(a.Config.Destination),
}

a.Config.ShowPath = os.ExpandEnv(a.Config.ShowPath)

a.CheckPaths()
a.Config.WatchList = []*matcher.MatchRequest{}

var filter, exclude matcher.Filter

if a.Config.TitleFilter != "" {
filter = matcher.Filter{regexp.MustCompile(a.Config.TitleFilter)}
filter = matcher.Filter{Regexp: regexp.MustCompile(a.Config.TitleFilter)}
}
if a.Config.TitleExclude != "" {
exclude = matcher.Filter{regexp.MustCompile(a.Config.TitleExclude)}
exclude = matcher.Filter{Regexp: regexp.MustCompile(a.Config.TitleExclude)}
}

for dl := 1; dl < flag.NArg(); dl++ {
Expand All @@ -234,6 +252,7 @@ func (a *app) Download(ctx context.Context) {
RetentionDays: a.Config.RetentionDays,
TitleFilter: filter,
TitleExclude: exclude,
ShowRootPath: a.Config.ShowPath,
},
)
}
Expand Down Expand Up @@ -377,22 +396,23 @@ showLoop:
}
seen[m.ID] = true

mediaBasePath := filepath.Base(m.Metadata.GetMediaPath(m.Match.ShowRootPath))
select {
case <-ctx.Done():
a.logger.Trace().Printf("[%s] Context done, received %s", p.Name(), ctx.Err())
break showLoop
default:
if !m.Metadata.Accepted(m.Match) {
a.logger.Trace().Printf("[%s] %s is filtered out.", p.Name(), filepath.Base(m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination])))
a.logger.Trace().Printf("[%s] %s is filtered out.", p.Name(), mediaBasePath)
} else if a.Config.Force || a.MustDownload(ctx, p, m) {
a.logger.Trace().Printf("[%s] Download of %q submitted", p.Name(), filepath.Base(m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination])))
a.logger.Trace().Printf("[%s] Download of %q submitted", p.Name(), mediaBasePath)
showCount++
if !a.Config.Headless {
providerBar.SetTotal(showCount, false)
}
a.SubmitDownload(ctx, &wg, p, m, pc, providerBar)
} else {
a.logger.Trace().Printf("[%s] %s already downloaded.", p.Name(), filepath.Base(m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination])))
a.logger.Trace().Printf("[%s] %s already downloaded.", p.Name(), mediaBasePath)
}
if ctx.Err() != nil {
a.logger.Debug().Printf("[%s] PullShows received %s", p.Name(), ctx.Err())
Expand All @@ -416,13 +436,13 @@ showLoop:

// MustDownload check if the show isn't yet downloaded.
func (a *app) MustDownload(ctx context.Context, p providers.Provider, m *providers.Media) bool {
mediaPath := m.Metadata.GetMediaPath(a.Config.Destinations[m.Match.Destination])
mediaPath := m.Metadata.GetMediaPath(m.Match.ShowRootPath)
mediaExists, err := fileExists(mediaPath)
if mediaExists {
return false
}

mediaPath = m.Metadata.GetMediaPathMatcher(a.Config.Destinations[m.Match.Destination])
mediaPath = m.Metadata.GetMediaPathMatcher(m.Match.ShowRootPath)
files, err := filepath.Glob(mediaPath)
if err != nil {
log.Fatalf("Can't glob %s: %v", mediaPath, err)
Expand Down

0 comments on commit 6e9a153

Please sign in to comment.