From 298063553c3b8eca5c2732e695c0cc431b1b85c0 Mon Sep 17 00:00:00 2001 From: Quentin Lemaire Date: Wed, 8 Apr 2020 14:40:35 +0200 Subject: [PATCH] feat(command): Init command --- client.go | 4 + client_test.go | 2 + command.go | 115 +++++++++++++++ command_test.go | 338 +++++++++++++++++++++++++++++++++++++++++++ command_type.go | 72 +++++++++ command_type_test.go | 95 ++++++++++++ disk.go | 4 +- movie.go | 4 +- status.go | 4 +- 9 files changed, 629 insertions(+), 9 deletions(-) create mode 100644 command.go create mode 100644 command_test.go create mode 100644 command_type.go create mode 100644 command_type_test.go diff --git a/client.go b/client.go index f5c2fcd..3a3e93c 100644 --- a/client.go +++ b/client.go @@ -32,6 +32,7 @@ func New(radarrURL, apiKey string, client HTTPClientInterface) (*Service, error) s.Movies = newMovieService(s) s.SystemStatus = newSystemStatusService(s) s.Diskspace = newDiskspaceService(s) + s.Command = newCommandService(s) return s, nil } @@ -52,4 +53,7 @@ type Service struct { // https://github.com/Radarr/Radarr/wiki/API:Diskspace Diskspace *DiskspaceService + + // https://github.com/Radarr/Radarr/wiki/API:Command + Command *CommandService } diff --git a/client_test.go b/client_test.go index d2fb1ac..fe7f05c 100644 --- a/client_test.go +++ b/client_test.go @@ -20,6 +20,7 @@ func TestNew(t *testing.T) { serviceWithCustomHTTPClient.Movies = newMovieService(serviceWithCustomHTTPClient) serviceWithCustomHTTPClient.Diskspace = newDiskspaceService(serviceWithCustomHTTPClient) serviceWithCustomHTTPClient.SystemStatus = newSystemStatusService(serviceWithCustomHTTPClient) + serviceWithCustomHTTPClient.Command = newCommandService(serviceWithCustomHTTPClient) client := http.Client{} client.Timeout = time.Second * 10 @@ -28,6 +29,7 @@ func TestNew(t *testing.T) { serviceWithDefaultHTTPClient.Movies = newMovieService(serviceWithDefaultHTTPClient) serviceWithDefaultHTTPClient.Diskspace = newDiskspaceService(serviceWithDefaultHTTPClient) serviceWithDefaultHTTPClient.SystemStatus = newSystemStatusService(serviceWithDefaultHTTPClient) + serviceWithDefaultHTTPClient.Command = newCommandService(serviceWithDefaultHTTPClient) tests := []struct { name string diff --git a/command.go b/command.go new file mode 100644 index 0000000..fea1873 --- /dev/null +++ b/command.go @@ -0,0 +1,115 @@ +package radarr + +import ( + "time" +) + +// Command describe a generic command +type Command struct { + Name string `json:"name"` + Body struct { + SendUpdatesToClient bool `json:"sendUpdatesToClient"` + UpdateScheduledTask bool `json:"updateScheduledTask"` + CompletionMessage string `json:"completionMessage"` + Name string `json:"name"` + Trigger string `json:"trigger"` + } `json:"body"` + Priority string `json:"priority"` + Status string `json:"status"` + Queued time.Time `json:"queued"` + Trigger string `json:"trigger"` + State string `json:"state"` + Manual bool `json:"manual"` + StartedOn time.Time `json:"startedOn"` + StateChangeTime time.Time `json:"stateChangeTime"` + SendUpdatesToClient bool `json:"sendUpdatesToClient"` + UpdateScheduledTask bool `json:"updateScheduledTask"` + ID int `json:"id"` +} + +type Tasks []Command + +// CommandService not usable for now +// contains Radarr commands operations +type CommandService struct { + s *Service +} + +func newCommandService(s *Service) *CommandService { + return &CommandService{s} +} + +// Status Queries the status of a previously started command mathing given unique ID +func (c *CommandService) Status(commandID string) *Command { + return nil +} + +// StatusAll Queries the status of all currently started commands. +func (c *CommandService) StatusAll() *Tasks { + return nil +} + +// RefreshMovie Refresh movie information from TMDb and rescan disk +func (c *CommandService) RefreshMovie(movieID ...int) *Command { + // name := "RefreshMovie" + return nil +} + +// RescanMovie Rescan disk for movies +func (c *CommandService) RescanMovie(movieID ...int) *Command { + // name := "RescanMovie" + return nil +} + +// MoviesSearch Search for one or more movies +func (c *CommandService) MoviesSearch(movieIDs ...[]int) *Command { + // name := "MoviesSearch" + return nil +} + +// DownloadedMoviesScan Instruct Radarr to scan the DroneFactoryFolder or a folder defined by the path variable. +// Each file and folder in the DroneFactoryFolder is interpreted as separate download. +// But a folder specified by the path variable is assumed to be a single download (job) and the folder name should be the release name. +// The downloadClientId can be used to support this API endpoint in conjunction with Completed Download Handling, so Radarr knows that a particular download has already been imported. +func (c *CommandService) DownloadedMoviesScan(opts ...*DownloadedMoviesScanOptions) *Command { + // name := "DownloadedMoviesScan" + return nil +} + +// RssSync Instruct Radarr to perform an RSS sync with all enabled indexers +func (c *CommandService) RssSync() *Command { + // name := "RssSync" + return nil +} + +// RenameFiles Instruct Radarr to rename the list of files provided. +func (c *CommandService) RenameFiles(files ...[]int) *Command { + // name := "RenameFiles" + return nil +} + +// RenameMovie Instruct Radarr to rename all files in the provided movies. +func (c *CommandService) RenameMovie(movieIDs ...[]int) *Command { + // name := "RenameMovie" + return nil +} + +// CutOffUnmetMoviesSearch Instructs Radarr to search all cutoff unmet movies (Take care, since it could go over your indexers api limits!) +func (c *CommandService) CutOffUnmetMoviesSearch(filter *Filter) *Command { + // name := "CutOffUnmetMoviesSearch" + return nil +} + +// NetImportSync Instructs Radarr to search all lists for movies not yet added to Radarr. +func (c *CommandService) NetImportSync() *Command { + // name := "NetImportSync" + return nil +} + +// MissingMoviesSearch Instructs Radarr to search all missing movies. +// This functionality is similar to what CouchPotato does and runs a backlog search for all your missing movies. +// For example You can use this api with curl and crontab to instruct Radarr to run a backlog search on 1 AM everyday. +func (c *CommandService) MissingMoviesSearch(filter *Filter) *Command { + // name := "MissingMoviesSearch" + return nil +} diff --git a/command_test.go b/command_test.go new file mode 100644 index 0000000..2f37071 --- /dev/null +++ b/command_test.go @@ -0,0 +1,338 @@ +package radarr + +import ( + "reflect" + "testing" +) + +func Test_newCommandService(t *testing.T) { + type args struct { + s *Service + } + tests := []struct { + name string + args args + want *CommandService + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newCommandService(tt.args.s); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newCommandService() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_Status(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + commandID string + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.Status(tt.args.commandID); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.Status() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_StatusAll(t *testing.T) { + type fields struct { + s *Service + } + tests := []struct { + name string + fields fields + want *Tasks + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.StatusAll(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.StatusAll() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_RefreshMovie(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + movieID []int + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.RefreshMovie(tt.args.movieID...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.RefreshMovie() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_RescanMovie(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + movieID []int + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.RescanMovie(tt.args.movieID...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.RescanMovie() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_MoviesSearch(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + movieIDs [][]int + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.MoviesSearch(tt.args.movieIDs...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.MoviesSearch() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_DownloadedMoviesScan(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + opts []*DownloadedMoviesScanOptions + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.DownloadedMoviesScan(tt.args.opts...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.DownloadedMoviesScan() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_RssSync(t *testing.T) { + type fields struct { + s *Service + } + tests := []struct { + name string + fields fields + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.RssSync(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.RssSync() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_RenameFiles(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + files [][]int + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.RenameFiles(tt.args.files...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.RenameFiles() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_RenameMovie(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + movieIDs [][]int + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.RenameMovie(tt.args.movieIDs...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.RenameMovie() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_CutOffUnmetMoviesSearch(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + filter *Filter + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.CutOffUnmetMoviesSearch(tt.args.filter); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.CutOffUnmetMoviesSearch() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_NetImportSync(t *testing.T) { + type fields struct { + s *Service + } + tests := []struct { + name string + fields fields + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.NetImportSync(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.NetImportSync() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCommandService_MissingMoviesSearch(t *testing.T) { + type fields struct { + s *Service + } + type args struct { + filter *Filter + } + tests := []struct { + name string + fields fields + args args + want *Command + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CommandService{ + s: tt.fields.s, + } + if got := c.MissingMoviesSearch(tt.args.filter); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CommandService.MissingMoviesSearch() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/command_type.go b/command_type.go new file mode 100644 index 0000000..c36f323 --- /dev/null +++ b/command_type.go @@ -0,0 +1,72 @@ +package radarr + +// ImportMode can be used to override the default Copy for torrents with external preprocessing/transcoding/unrar. +type ImportMode int + +const ( + // Move imported files instead of copy + Move ImportMode = iota + + // Copy Or Hardlink depending on Radarr configuration + Copy +) + +type DownloadedMoviesScanOptions struct { + Path string `json:"path"` + DownloadClientID string `json:"downloadClientId"` + IportMode ImportMode `json:"importMode"` +} + +var availableImportMode [2]string = [2]string{"Move", "Copy"} + +func (i ImportMode) String() string { + return availableImportMode[i] +} + +type filter struct { + Key string `json:"filterKey"` + Value interface{} `json:"filterValue"` +} + +// Here the list of all filters for movies +// If you have a better idea, please tell me +// I've been looking for every possible solution for seven hours... :'( +var availableFilter [7]filter = [7]filter{ + filter{Key: "monitored", Value: true}, + filter{Key: "monitored", Value: false}, + filter{Key: "all", Value: "all"}, + filter{Key: "status", Value: "available"}, + filter{Key: "status", Value: "released"}, + filter{Key: "status", Value: "inCinemas"}, + filter{Key: "status", Value: "announced"}, +} + +// Filter filtering options when using MissingMoviesSearch and CutOffUnmetMoviesSearchCommand +type Filter int + +const ( + // FilterByMonitored filter movies by monitored ones + FilterByMonitored Filter = iota + + // FilterByNonMonitored filter movies by non monitored ones + FilterByNonMonitored + + // FilterAll return all movies without filters + FilterAll + + // FilterByStatusAndAvailable return 'availables' movies + FilterByStatusAndAvailable + + // FilterByStatusAndReleased return 'released' movies + FilterByStatusAndReleased + + // FilterByStatusAndInCinemas return 'inCinemas' movies + FilterByStatusAndInCinemas + + // FilterByStatusAndAnnounced return 'announced' movies + FilterByStatusAndAnnounced +) + +func (f Filter) get() *filter { + return &availableFilter[f] +} diff --git a/command_type_test.go b/command_type_test.go new file mode 100644 index 0000000..02413d4 --- /dev/null +++ b/command_type_test.go @@ -0,0 +1,95 @@ +package radarr + +import ( + "reflect" + "testing" +) + +func TestFilter_get(t *testing.T) { + tests := []struct { + name string + f Filter + want *filter + }{ + struct { + name string + f Filter + want *filter + }{ + name: "FilterByMonitored", + f: FilterByMonitored, + want: &availableFilter[FilterByMonitored], + }, + { + name: "FilterByNonMonitored", + f: FilterByNonMonitored, + want: &availableFilter[FilterByNonMonitored], + }, + { + name: "FilterAll", + f: FilterAll, + want: &availableFilter[FilterAll], + }, + { + name: "FilterByStatusAndAvailable", + f: FilterByStatusAndAvailable, + want: &availableFilter[FilterByStatusAndAvailable], + }, + { + name: "FilterByStatusAndReleased", + f: FilterByStatusAndReleased, + want: &availableFilter[FilterByStatusAndReleased], + }, + { + name: "FilterByStatusAndInCinemas", + f: FilterByStatusAndInCinemas, + want: &availableFilter[FilterByStatusAndInCinemas], + }, + { + name: "FilterByStatusAndAnnounced", + f: FilterByStatusAndAnnounced, + want: &availableFilter[FilterByStatusAndAnnounced], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.f.get(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Filter.get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestImportMode_get(t *testing.T) { + tests := []struct { + name string + i ImportMode + want string + }{ + struct { + name string + i ImportMode + want string + }{ + name: "Move", + i: Move, + want: availableImportMode[Move], + }, + struct { + name string + i ImportMode + want string + }{ + name: "Copy", + i: Copy, + want: availableImportMode[Copy], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.i.String(); got != tt.want { + t.Errorf("ImportMode.get() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/disk.go b/disk.go index 1b6f5f9..062bfd5 100644 --- a/disk.go +++ b/disk.go @@ -22,9 +22,7 @@ type DiskspaceService struct { } func newDiskspaceService(s *Service) *DiskspaceService { - return &DiskspaceService{ - s: s, - } + return &DiskspaceService{s} } // Get return Radarr disk space info diff --git a/movie.go b/movie.go index de5c9a5..1726e06 100644 --- a/movie.go +++ b/movie.go @@ -126,9 +126,7 @@ type MovieService struct { } func newMovieService(s *Service) *MovieService { - return &MovieService{ - s: s, - } + return &MovieService{s} } // UpcomingOptions describe period to search upcoming movies with diff --git a/status.go b/status.go index 9283c2c..ff05e73 100644 --- a/status.go +++ b/status.go @@ -38,9 +38,7 @@ type SystemStatusService struct { } func newSystemStatusService(s *Service) *SystemStatusService { - return &SystemStatusService{ - s: s, - } + return &SystemStatusService{s} } // Get https://github.com/Radarr/Radarr/wiki/API:System-Status#get