Skip to content

Commit 6f982e1

Browse files
committed
feat: Implement reserves and encodes subcommands
This commit introduces the and subcommands to the CLI tool. Key changes include: - Implementation of , , , , and commands. - Implementation of , , and commands. - Updates to the API client wrapper () to support new API calls. - Refinements to output formatting functions in . - Addressing compilation errors and ensuring a successful build. Note: Creating reserves by program ID is not directly supported by the EPGStation API for manual reserves; they are created with encode options.
1 parent 04ec057 commit 6f982e1

File tree

5 files changed

+633
-0
lines changed

5 files changed

+633
-0
lines changed

cmd/epgstationctl/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package main
22

33
import (
44
_ "github.com/miscord-dev/epgstationctl/internal/commands/channels"
5+
_ "github.com/miscord-dev/epgstationctl/internal/commands/encodes"
56
_ "github.com/miscord-dev/epgstationctl/internal/commands/programs"
67
_ "github.com/miscord-dev/epgstationctl/internal/commands/recordings"
8+
_ "github.com/miscord-dev/epgstationctl/internal/commands/reserves"
79
"github.com/miscord-dev/epgstationctl/internal/commands/root"
810
_ "github.com/miscord-dev/epgstationctl/internal/commands/rules"
911
)

internal/client/wrapper.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,143 @@ func (c *EPGStationClient) SearchRulesKeyword(params *epgstation.GetRulesKeyword
297297

298298
return &keywords, nil
299299
}
300+
301+
// GetReserves retrieves current reserves with optional filtering
302+
func (c *EPGStationClient) GetReserves(params *epgstation.GetReservesParams) (*epgstation.Reserves, error) {
303+
resp, err := c.client.GetReserves(context.Background(), params)
304+
if err != nil {
305+
return nil, err
306+
}
307+
defer func() { _ = resp.Body.Close() }()
308+
309+
if resp.StatusCode != http.StatusOK {
310+
return nil, handleErrorResponse(resp)
311+
}
312+
313+
var reserves epgstation.Reserves
314+
if err := parseJSONResponse(resp, &reserves); err != nil {
315+
return nil, err
316+
}
317+
318+
return &reserves, nil
319+
}
320+
321+
// CreateReserve creates a manual reserve
322+
func (c *EPGStationClient) CreateReserve(body epgstation.ManualReserveOption) (*epgstation.AddedReserve, error) {
323+
resp, err := c.client.PostReserves(context.Background(), body)
324+
if err != nil {
325+
return nil, err
326+
}
327+
defer func() { _ = resp.Body.Close() }()
328+
329+
if resp.StatusCode != http.StatusCreated {
330+
return nil, handleErrorResponse(resp)
331+
}
332+
333+
var result epgstation.AddedReserve
334+
if err := parseJSONResponse(resp, &result); err != nil {
335+
return nil, err
336+
}
337+
338+
return &result, nil
339+
}
340+
341+
// UpdateReserve updates an existing reserve
342+
func (c *EPGStationClient) UpdateReserve(reserveID int, body epgstation.EditManualReserveOption) error {
343+
resp, err := c.client.PutReservesReserveId(context.Background(), reserveID, body)
344+
if err != nil {
345+
return err
346+
}
347+
defer func() { _ = resp.Body.Close() }()
348+
349+
if resp.StatusCode != http.StatusOK {
350+
return handleErrorResponse(resp)
351+
}
352+
353+
return nil
354+
}
355+
356+
// DeleteReserve deletes a reserve
357+
func (c *EPGStationClient) DeleteReserve(reserveID int) error {
358+
resp, err := c.client.DeleteReservesReserveId(context.Background(), reserveID)
359+
if err != nil {
360+
return err
361+
}
362+
defer func() { _ = resp.Body.Close() }()
363+
364+
if resp.StatusCode != http.StatusOK {
365+
return handleErrorResponse(resp)
366+
}
367+
368+
return nil
369+
}
370+
371+
// UnSkipReserve removes skip status from a reserve
372+
func (c *EPGStationClient) UnSkipReserve(reserveID int) error {
373+
resp, err := c.client.DeleteReservesReserveIdSkip(context.Background(), reserveID)
374+
if err != nil {
375+
return err
376+
}
377+
defer func() { _ = resp.Body.Close() }()
378+
379+
if resp.StatusCode != http.StatusOK {
380+
return handleErrorResponse(resp)
381+
}
382+
383+
return nil
384+
}
385+
386+
// GetEncodes retrieves encode jobs with optional filtering
387+
func (c *EPGStationClient) GetEncodes(params *epgstation.GetEncodeParams) (*epgstation.EncodeInfo, error) {
388+
resp, err := c.client.GetEncode(context.Background(), params)
389+
if err != nil {
390+
return nil, err
391+
}
392+
defer func() { _ = resp.Body.Close() }()
393+
394+
if resp.StatusCode != http.StatusOK {
395+
return nil, handleErrorResponse(resp)
396+
}
397+
398+
var encodes epgstation.EncodeInfo
399+
if err := parseJSONResponse(resp, &encodes); err != nil {
400+
return nil, err
401+
}
402+
403+
return &encodes, nil
404+
}
405+
406+
// CreateEncode creates a manual encode job
407+
func (c *EPGStationClient) CreateEncode(body epgstation.AddManualEncodeProgramOption) (*epgstation.AddedEncode, error) {
408+
resp, err := c.client.PostEncode(context.Background(), body)
409+
if err != nil {
410+
return nil, err
411+
}
412+
defer func() { _ = resp.Body.Close() }()
413+
414+
if resp.StatusCode != http.StatusCreated {
415+
return nil, handleErrorResponse(resp)
416+
}
417+
418+
var result epgstation.AddedEncode
419+
if err := parseJSONResponse(resp, &result); err != nil {
420+
return nil, err
421+
}
422+
423+
return &result, nil
424+
}
425+
426+
// DeleteEncode deletes/cancels an encode job
427+
func (c *EPGStationClient) DeleteEncode(encodeID int) error {
428+
resp, err := c.client.DeleteEncodeEncodeId(context.Background(), encodeID)
429+
if err != nil {
430+
return err
431+
}
432+
defer func() { _ = resp.Body.Close() }()
433+
434+
if resp.StatusCode != http.StatusOK {
435+
return handleErrorResponse(resp)
436+
}
437+
438+
return nil
439+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package encodes
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
7+
"github.com/miscord-dev/epgstationctl/internal/client"
8+
"github.com/miscord-dev/epgstationctl/internal/commands/root"
9+
"github.com/miscord-dev/epgstationctl/internal/epgstation"
10+
"github.com/miscord-dev/epgstationctl/internal/output"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
const (
15+
outputFormatJSON = "json"
16+
)
17+
18+
var (
19+
offset int
20+
limit int
21+
isEncoding bool
22+
23+
// Create encode flags
24+
recordedID int
25+
encodeMode int
26+
encodeParentDir string
27+
encodeDir string
28+
removeOriginal bool
29+
)
30+
31+
var encodesCmd = &cobra.Command{
32+
Use: "encodes",
33+
Short: "Manage encoding jobs",
34+
Long: "Commands for managing EPGStation encoding jobs",
35+
}
36+
37+
var listCmd = &cobra.Command{
38+
Use: "list",
39+
Short: "List encoding jobs",
40+
Long: "List all encoding jobs with their status",
41+
RunE: func(cmd *cobra.Command, args []string) error {
42+
cfg := root.GetConfig()
43+
client, err := client.NewEPGStationClient(cfg)
44+
if err != nil {
45+
return fmt.Errorf("failed to create EPGStation client: %w", err)
46+
}
47+
48+
encodes, err := client.GetEncodes(&epgstation.GetEncodeParams{IsHalfWidth: false}) // Assuming IsHalfWidth is required
49+
if err != nil {
50+
return fmt.Errorf("failed to get encodes: %w", err)
51+
}
52+
53+
if cfg.Output.Format == outputFormatJSON {
54+
return output.PrintAsJSON(encodes)
55+
}
56+
57+
return printEncodesTable(encodes)
58+
},
59+
}
60+
61+
var createCmd = &cobra.Command{
62+
Use: "create",
63+
Short: "Create a manual encode job",
64+
Long: "Create a manual encoding job for a recorded program",
65+
RunE: func(cmd *cobra.Command, args []string) error {
66+
if recordedID == 0 {
67+
return fmt.Errorf("recorded ID is required (use --recorded-id)")
68+
}
69+
70+
cfg := root.GetConfig()
71+
client, err := client.NewEPGStationClient(cfg)
72+
if err != nil {
73+
return fmt.Errorf("failed to create EPGStation client: %w", err)
74+
}
75+
76+
encodeOption := epgstation.AddManualEncodeProgramOption{
77+
RecordedId: recordedID,
78+
Mode: strconv.Itoa(encodeMode), // Mode is string
79+
}
80+
81+
if encodeParentDir != "" {
82+
encodeOption.ParentDir = &encodeParentDir
83+
}
84+
if encodeDir != "" {
85+
encodeOption.Directory = &encodeDir
86+
}
87+
encodeOption.RemoveOriginal = removeOriginal // RemoveOriginal is bool, not *bool
88+
89+
result, err := client.CreateEncode(encodeOption)
90+
if err != nil {
91+
return fmt.Errorf("failed to create encode: %w", err)
92+
}
93+
94+
if cfg.Output.Format == outputFormatJSON {
95+
return output.PrintAsJSON(result)
96+
}
97+
98+
fmt.Printf("Encode job created successfully with ID: %d\n", result.EncodeId)
99+
return nil
100+
},
101+
}
102+
103+
var deleteCmd = &cobra.Command{
104+
Use: "delete <id>",
105+
Short: "Delete an encode job",
106+
Long: "Delete/cancel an encoding job",
107+
Args: cobra.ExactArgs(1),
108+
RunE: func(cmd *cobra.Command, args []string) error {
109+
encodeID, err := strconv.Atoi(args[0])
110+
if err != nil {
111+
return fmt.Errorf("invalid encode ID: %s", args[0])
112+
}
113+
114+
cfg := root.GetConfig()
115+
client, err := client.NewEPGStationClient(cfg)
116+
if err != nil {
117+
return fmt.Errorf("failed to create EPGStation client: %w", err)
118+
}
119+
120+
err = client.DeleteEncode(encodeID)
121+
if err != nil {
122+
return fmt.Errorf("failed to delete encode: %w", err)
123+
}
124+
125+
fmt.Printf("Encode job %d deleted successfully\n", encodeID)
126+
return nil
127+
},
128+
}
129+
130+
func printEncodesTable(encodes *epgstation.EncodeInfo) error {
131+
table := output.NewTable()
132+
table.SetHeader([]string{"ID", "Name", "Mode", "Status", "Progress", "Started"})
133+
134+
for _, encode := range encodes.RunningItems {
135+
progress := "N/A"
136+
if encode.Percent != nil {
137+
progress = fmt.Sprintf("%.1f%%", *encode.Percent)
138+
}
139+
140+
table.Append([]string{
141+
strconv.Itoa(encode.Id),
142+
encode.Recorded.Name,
143+
encode.Mode,
144+
"Encoding",
145+
progress,
146+
output.FormatUnixTime(encode.Recorded.StartAt),
147+
})
148+
}
149+
150+
for _, encode := range encodes.WaitItems {
151+
table.Append([]string{
152+
strconv.Itoa(encode.Id),
153+
encode.Recorded.Name,
154+
encode.Mode,
155+
"Waiting",
156+
"N/A",
157+
output.FormatUnixTime(encode.Recorded.StartAt),
158+
})
159+
}
160+
161+
table.Render()
162+
return nil
163+
}
164+
165+
func init() {
166+
// Create command flags
167+
createCmd.Flags().IntVar(&recordedID, "recorded-id", 0, "Recorded program ID to encode (required)")
168+
createCmd.Flags().IntVar(&encodeMode, "encode-mode", 0, "Encode mode ID")
169+
createCmd.Flags().StringVar(&encodeParentDir, "encode-parent-directory", "", "Encode parent directory")
170+
createCmd.Flags().StringVar(&encodeDir, "encode-directory", "", "Encode directory")
171+
createCmd.Flags().BoolVar(&removeOriginal, "remove-original", false, "Remove original file after encoding")
172+
createCmd.MarkFlagRequired("recorded-id")
173+
createCmd.MarkFlagRequired("encode-mode") // Make encode-mode required
174+
175+
// Add subcommands
176+
encodesCmd.AddCommand(listCmd)
177+
encodesCmd.AddCommand(createCmd)
178+
encodesCmd.AddCommand(deleteCmd)
179+
180+
// Register with root command
181+
root.AddCommand(encodesCmd)
182+
}

0 commit comments

Comments
 (0)