diff --git a/cmd/commands.go b/cmd/commands.go index e812906..47def3e 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -105,7 +105,6 @@ Default shows queued movies only.`, return c.handler.Remove(cmd.Context(), args[0]) }, }) - return root } @@ -150,6 +149,7 @@ Use the -i flag for an interactive interface with navigation keys.`, addCmd.Flags().BoolP("interactive", "i", false, "Use interactive interface for TV show selection") root.AddCommand(addCmd) + // TODO: Add interactive list view root.AddCommand(&cobra.Command{ Use: "list [--all|--queued|--watching|--watched]", Short: "List TV shows in queue with status filtering", diff --git a/internal/services/atproto.go b/internal/services/atproto.go index 62b0273..6457444 100644 --- a/internal/services/atproto.go +++ b/internal/services/atproto.go @@ -24,69 +24,17 @@ import ( "github.com/stormlightlabs/noteleaf/internal/public" ) +type MutateRecordOutput struct { + Cid string `json:"cid"` + Uri string `json:"uri"` +} + // DocumentWithMeta combines a document with its repository metadata type DocumentWithMeta struct { Document public.Document Meta public.DocumentMeta } -// convertCBORToJSONCompatible recursively converts CBOR data structures to JSON-compatible types -// -// This converts map[any]any to map[string]any to allow usage of [json.Marshal] -func convertCBORToJSONCompatible(data any) any { - switch v := data.(type) { - case map[any]any: - result := make(map[string]any, len(v)) - for key, value := range v { - strKey := fmt.Sprintf("%v", key) - result[strKey] = convertCBORToJSONCompatible(value) - } - return result - case map[string]any: - result := make(map[string]any, len(v)) - for key, value := range v { - result[key] = convertCBORToJSONCompatible(value) - } - return result - case []any: - result := make([]any, len(v)) - for i, item := range v { - result[i] = convertCBORToJSONCompatible(item) - } - return result - default: - return v - } -} - -// convertJSONToCBORCompatible recursively converts JSON-compatible data structures to CBOR types -// -// This converts map[string]any to map[any]any to allow proper CBOR encoding for AT Protocol -func convertJSONToCBORCompatible(data any) any { - switch v := data.(type) { - case map[string]any: - result := make(map[any]any, len(v)) - for key, value := range v { - result[key] = convertJSONToCBORCompatible(value) - } - return result - case map[any]any: - result := make(map[any]any, len(v)) - for key, value := range v { - result[key] = convertJSONToCBORCompatible(value) - } - return result - case []any: - result := make([]any, len(v)) - for i, item := range v { - result[i] = convertJSONToCBORCompatible(item) - } - return result - default: - return v - } -} - // PublicationWithMeta combines a publication with its metadata type PublicationWithMeta struct { Publication public.Publication @@ -476,7 +424,6 @@ func (s *ATProtoService) PostDocument(ctx context.Context, doc public.Document, parts := strings.Split(output.Uri, "/") rkey := parts[len(parts)-1] - meta := public.DocumentMeta{ RKey: rkey, CID: output.Cid, @@ -484,11 +431,7 @@ func (s *ATProtoService) PostDocument(ctx context.Context, doc public.Document, IsDraft: isDraft, FetchedAt: time.Now(), } - - return &DocumentWithMeta{ - Document: doc, - Meta: meta, - }, nil + return &DocumentWithMeta{Document: doc, Meta: meta}, nil } // PatchDocument updates an existing document in the user's repository @@ -511,43 +454,23 @@ func (s *ATProtoService) PatchDocument(ctx context.Context, rkey string, doc pub } doc.Type = collection - jsonBytes, err := json.Marshal(doc) if err != nil { - return nil, fmt.Errorf("failed to marshal document to JSON: %w", err) - } - - var jsonData map[string]any - if err := json.Unmarshal(jsonBytes, &jsonData); err != nil { - return nil, fmt.Errorf("failed to unmarshal JSON to map: %w", err) - } - - cborCompatible := convertJSONToCBORCompatible(jsonData) - - cborBytes, err := cbor.Marshal(cborCompatible) - if err != nil { - return nil, fmt.Errorf("failed to marshal to CBOR: %w", err) - } - - record := &lexutil.LexiconTypeDecoder{} - if err := cbor.Unmarshal(cborBytes, record); err != nil { - return nil, fmt.Errorf("failed to unmarshal CBOR to lexicon type: %w", err) + return nil, fmt.Errorf("marshal: %w", err) } - input := &atproto.RepoPutRecord_Input{ - Repo: s.session.DID, - Collection: collection, - Rkey: rkey, - Record: record, + var m map[string]any + if err := json.Unmarshal(jsonBytes, &m); err != nil { + return nil, fmt.Errorf("unmarshal: %w", err) } + m["$type"] = collection - output, err := atproto.RepoPutRecord(ctx, s.client, input) + output, err := repoPutRecord(ctx, s.client, s.session.DID, collection, rkey, m) if err != nil { return nil, fmt.Errorf("failed to update record: %w", err) } uri := fmt.Sprintf("at://%s/%s/%s", s.session.DID, collection, rkey) - meta := public.DocumentMeta{ RKey: rkey, CID: output.Cid, @@ -555,11 +478,7 @@ func (s *ATProtoService) PatchDocument(ctx context.Context, rkey string, doc pub IsDraft: isDraft, FetchedAt: time.Now(), } - - return &DocumentWithMeta{ - Document: doc, - Meta: meta, - }, nil + return &DocumentWithMeta{Document: doc, Meta: meta}, nil } // DeleteDocument removes a document from the user's repository @@ -625,19 +544,14 @@ func (s *ATProtoService) Close() error { return nil } -type RepoCreateRecordOutput struct { - Cid string `json:"cid"` - Uri string `json:"uri"` -} - -func repoCreateRecord(ctx context.Context, client *xrpc.Client, repo, collection string, record map[string]any) (*RepoCreateRecordOutput, error) { +func repoCreateRecord(ctx context.Context, client *xrpc.Client, repo, collection string, record map[string]any) (*MutateRecordOutput, error) { body := map[string]any{ "repo": repo, "collection": collection, "record": record, } - var out RepoCreateRecordOutput + var out MutateRecordOutput if err := client.LexDo( ctx, lexutil.Procedure, @@ -651,3 +565,83 @@ func repoCreateRecord(ctx context.Context, client *xrpc.Client, repo, collection } return &out, nil } + +func repoPutRecord(ctx context.Context, client *xrpc.Client, repo, collection, rkey string, record map[string]any) (*MutateRecordOutput, error) { + body := map[string]any{ + "repo": repo, + "collection": collection, + "rkey": rkey, + "record": record, + } + + var out MutateRecordOutput + if err := client.LexDo( + ctx, + lexutil.Procedure, + "application/json", + "com.atproto.repo.putRecord", + nil, + body, + &out, + ); err != nil { + return nil, fmt.Errorf("repoPutRecord failed: %w", err) + } + return &out, nil +} + +// convertCBORToJSONCompatible recursively converts CBOR data structures to JSON-compatible types +// +// This converts map[any]any to map[string]any to allow usage of [json.Marshal] +func convertCBORToJSONCompatible(data any) any { + switch v := data.(type) { + case map[any]any: + result := make(map[string]any, len(v)) + for key, value := range v { + strKey := fmt.Sprintf("%v", key) + result[strKey] = convertCBORToJSONCompatible(value) + } + return result + case map[string]any: + result := make(map[string]any, len(v)) + for key, value := range v { + result[key] = convertCBORToJSONCompatible(value) + } + return result + case []any: + result := make([]any, len(v)) + for i, item := range v { + result[i] = convertCBORToJSONCompatible(item) + } + return result + default: + return v + } +} + +// convertJSONToCBORCompatible recursively converts JSON-compatible data structures to CBOR types +// +// This converts map[string]any to map[any]any to allow proper CBOR encoding for AT Protocol +func convertJSONToCBORCompatible(data any) any { + switch v := data.(type) { + case map[string]any: + result := make(map[any]any, len(v)) + for key, value := range v { + result[key] = convertJSONToCBORCompatible(value) + } + return result + case map[any]any: + result := make(map[any]any, len(v)) + for key, value := range v { + result[key] = convertJSONToCBORCompatible(value) + } + return result + case []any: + result := make([]any, len(v)) + for i, item := range v { + result[i] = convertJSONToCBORCompatible(item) + } + return result + default: + return v + } +} diff --git a/internal/services/samples/movie.html b/internal/services/samples/movie.html index f0d75c7..ee1d07d 100644 --- a/internal/services/samples/movie.html +++ b/internal/services/samples/movie.html @@ -1 +1 @@ -The Fantastic Four: First Steps | Rotten Tomatoes
☰
Rotten Tomatoes
Cancel Movies Tv shows RT App News Showtimes

The Fantastic Four: First Steps

Play trailer0:59Poster for The Fantastic Four: First StepsPG-13Now Playing1h 54mActionAdventureSci-FiFantasyPlayTrailer Watchlist
WatchlistTomatometerPopcornmeter
87%Tomatometer390 Reviews 91%Popcornmeter10,000+ Verified Ratings
Set against the vibrant backdrop of a 1960s-inspired, retro-futuristic world, Marvel Studios' "The Fantastic Four: First Steps" introduces Marvel's First Family--Reed Richards/Mister Fantastic (Pedro Pascal), Sue Storm/Invisible Woman (Vanessa Kirby), Johnny Storm/Human Torch (Joseph Quinn) and Ben Grimm/The Thing (Ebon Moss-Bachrach) as they face their most daunting challenge yet. Forced to balance their roles as heroes with the strength of their family bond, they must defend Earth from a ravenous space god called Galactus (Ralph Ineson) and his enigmatic Herald, Silver Surfer (Julia Garner). And if Galactus' plan to devour the entire planet and everyone on it weren't bad enough, it suddenly gets very personal.
Now in TheatersNow PlayingBuy Tickets
The Fantastic Four: First Steps

What to Know

Critics Consensus

Benefitting from rock-solid cast chemistry and clad in appealingly retro 1960s design, this crack at The Fantastic Fourdoes Marvel's First Family justice.

Read Critics Reviews

Audience Says

The Fantastic Fourtakes the world by Storm, Thing, Reed, Johnny and baby, forging a new path for this bespoke family that, with these First Steps, leaps into cosmic action with retro-futuristic verve.

Read Audience Reviews

Critics Reviews

View More (390)
Dwight Brown DwightBrownInk.com Audiences shouldn’t have to sacrifice eye-catching stunts and imagery for great writing and acting. They should have it all. Should but won’t in this case. Rated: 2.5/4Aug 18, 2025 Full ReviewSergio Burstein Los Angeles Times For now, let's enjoy what this efficient commercial product has to offer, which surprisingly lasts less than two hours... [Full review in Spanish] Aug 11, 2025 Full ReviewMark Kermode Kermode and Mayo's Take (YouTube) Even if you don't like the film, it is absolutely worth it for the furniture. Aug 6, 2025 Full ReviewKevin Carr Fat Guys at the Movies It’s still a superhero movie, and it connects to the next phase of Marvel crossover films. However, it taps into some real human elements. Rated: 4/5Sep 4, 2025 Full ReviewNiall McCloskey Film Ireland Magazine If you’re looking for an electric superhero thrilling ride filled with compelling performances, great action and that classic comic flair, this may be four you. Sep 3, 2025 Full ReviewTroy Ribeiro Free Press Journal (India) In The Fantastic Four: First Steps, Marvel returns to the drawing board—this time armed with chalk, retro optimism, and the ever-dependable Pedro Pascal Rated: 2.5/5Aug 30, 2025 Full ReviewRead all reviews

Audience Reviews

View More (1000+)
Sanjeev Fantastic4 was okay. Choppy character development. Choppy story. Rated 2 out of 5 stars09/07/25Full Review Daniel Could have benefitted by adding battles with Mole Man and a few of the older FF4 rogues gallery Rated 4 out of 5 stars09/07/25Full Review Valentina The whole family enjoyed it 😃 Rated 4 out of 5 stars09/07/25Full Review DUANE S It was surprisingly entertaining Rated 4.5 out of 5 stars09/07/25Full Review Tish it was good but I feel like I missed something. Rated 4 out of 5 stars09/07/25Full Review David L Way too much back story and not enough action. Kept waiting for something to happen. Rated 1 out of 5 stars09/07/25Full Review Read all reviews
The Fantastic Four: First Steps

My Rating

Read MoreRead LessPOST RATING WRITE A REVIEW EDIT REVIEW

The Fantastic Four: First Steps

The Fantastic Four: First Steps: Movie Clip - I Herald GalactusThe Fantastic Four: First Steps: Movie Clip - I Herald Galactus1:01 The Fantastic Four: First Steps: Movie Clip - Sunday DinnerThe Fantastic Four: First Steps: Movie Clip - Sunday Dinner1:24 View more videos
Godzilla vs. Kong76% 91% Godzilla vs. Kong WatchlistTRAILERfor Godzilla vs. KongAvengers: Infinity War85% 92% Avengers: Infinity War WatchlistTRAILERfor Avengers: Infinity WarWar for the Planet of the Apes94% 84% War for the Planet of the Apes WatchlistTRAILERfor War for the Planet of the ApesGodzilla Minus One99% 98% Godzilla Minus One WatchlistTRAILERfor Godzilla Minus OneThe Creator67% 75% The Creator WatchlistTRAILERfor The CreatorDiscover more movies and TV shows.View More

Movie Info

SynopsisSet against the vibrant backdrop of a 1960s-inspired, retro-futuristic world, Marvel Studios' "The Fantastic Four: First Steps" introduces Marvel's First Family--Reed Richards/Mister Fantastic (Pedro Pascal), Sue Storm/Invisible Woman (Vanessa Kirby), Johnny Storm/Human Torch (Joseph Quinn) and Ben Grimm/The Thing (Ebon Moss-Bachrach) as they face their most daunting challenge yet. Forced to balance their roles as heroes with the strength of their family bond, they must defend Earth from a ravenous space god called Galactus (Ralph Ineson) and his enigmatic Herald, Silver Surfer (Julia Garner). And if Galactus' plan to devour the entire planet and everyone on it weren't bad enough, it suddenly gets very personal.
Director
Matt Shakman
Producer
Kevin Feige
Screenwriter
Peter Cameron
Distributor
Walt Disney Pictures
Production Co
Marvel Studios
Rating
PG-13 (Some Language|Action/Violence)
Genre
Action, Adventure, Sci-Fi, Fantasy
Original Language
English
Release Date (Theaters)
Jul 25, 2025, Wide
Box Office (Gross USA)
$266.4M
Runtime
1h 54m
Sound Mix
Dolby Atmos
Aspect Ratio
Digital 2.39:1
+The Fantastic Four: First Steps | Rotten Tomatoes
diff --git a/internal/services/samples/movie_search.html b/internal/services/samples/movie_search.html index b313409..3e44078 100644 --- a/internal/services/samples/movie_search.html +++ b/internal/services/samples/movie_search.html @@ -1 +1 @@ -Search Results | Rotten Tomatoes
☰
Rotten Tomatoes
Cancel Movies Tv shows RT App News Showtimes
+Search Results | Rotten Tomatoes
diff --git a/internal/services/samples/search.html b/internal/services/samples/search.html index 2e9ff23..00486c6 100644 --- a/internal/services/samples/search.html +++ b/internal/services/samples/search.html @@ -1 +1 @@ -Search Results | Rotten Tomatoes
☰
Rotten Tomatoes
Cancel Movies Tv shows RT App News Showtimes
+Search Results | Rotten Tomatoes
diff --git a/internal/services/samples/series_overview.html b/internal/services/samples/series_overview.html index 085f86e..5272c4e 100644 --- a/internal/services/samples/series_overview.html +++ b/internal/services/samples/series_overview.html @@ -1 +1 @@ -Peacemaker (2022) | Rotten Tomatoes
☰
Rotten Tomatoes
Cancel Movies Tv shows RT App News Showtimes

Peacemaker

Play trailer1:39Poster for PeacemakerTV-MANext Ep Thu Sep 112 SeasonsComedyActionPlayTrailer Watchlist
WatchlistTomatometerPopcornmeter
96%Avg. Tomatometer150 Reviews 84%Avg. Popcornmeter2,500+ Ratings
A man fights for peace at any cost, no matter how many people he has to kill to get it.
Watch on Fandango at HomeStream Now

Where to Watch

Peacemaker

Seasons

Season 299%Next Ep Thu Sep 11Details Season 193%2022Details
Twisted Metal79% 88% Twisted Metal WatchlistTRAILERfor Twisted MetalComrade Detective85% 88% Comrade Detective WatchlistSaul of the Mole Men17% % Saul of the Mole Men WatchlistSomebody Somewhere100% 93% Somebody Somewhere WatchlistTRAILERfor Somebody SomewhereSouth Side100% 92% South Side WatchlistTRAILERfor South SideDiscover more movies and TV shows.View More

Series Info

SynopsisA man fights for peace at any cost, no matter how many people he has to kill to get it.
Executive Producer
James Gunn, Peter Safran, John Cena
Screenwriter
James Gunn
Network
HBO Max
Rating
TV-MA
Genre
Comedy, Action
Original Language
English
Release Date
Jan 13, 2022
+Peacemaker (2022) | Rotten Tomatoes
diff --git a/internal/services/samples/series_season.html b/internal/services/samples/series_season.html index 1207506..82e2612 100644 --- a/internal/services/samples/series_season.html +++ b/internal/services/samples/series_season.html @@ -1 +1 @@ -Peacemaker: Season 2 | Rotten Tomatoes
☰
Rotten Tomatoes
Cancel Movies Tv shows RT App News Showtimes

Season 2 – Peacemaker

Play trailer1:39Poster for Season 2 – PeacemakerNext Ep Thu Sep 11ComedyActionPlayTrailer Watchlist
WatchlistTomatometerPopcornmeter
99%Tomatometer82 Reviews 80%Popcornmeter1,000+ Ratings
A man fights for peace at any cost, no matter how many people he has to kill to get it.
Watch on MaxStream Now

Where to Watch

Peacemaker — Season 2

Peacemaker — Season 2

What to Know

Critics Consensus

Peacemaker's second season goes multidimensional while still maintaining a singular focus on emotional stakes, seamlessly transporting this outrageous antihero into a fresh cinematic universe.

Read Critics Reviews

Critics Reviews

View More (82)
Craig Mathieson The Age (Australia) Dimensional travel, vengeful relatives and spurts of bloody violence set the tone for a series that remains a cartoonish change of pace. Rated: 3/5Aug 28, 2025 Full ReviewGraeme Virtue Guardian Gunn clearly loved these damaged characters enough to carry them over to his shiny new universe, but he is also never afraid to put them through the wringer. It makes even the tiniest victories in their lives feel momentous. Rated: 4/5Aug 25, 2025 Full ReviewSam Adams Slate Like the first season, the second season of Peacemaker risks stretching too few jokes over too many hours—even if sometimes the joke going on for too long is the joke. But it’s also strangely fascinating. Aug 25, 2025 Full ReviewDavid Craig Radio Times From the opening dance number, it's an enormous amount of fun that carefully balances its surreal pleasures with impactful character-led moments – and plenty of unexpected twists. Rated: 4/5Aug 30, 2025 Full ReviewAlex Maidy JoBlo's Movie Network While I do not suffer from bird blindness, I can say that Peacemaker soars with the ducks. Rated: 9/10Aug 28, 2025 Full ReviewJoel Keller Decider Peacemaker continues to be a funny but emotional superhero drama, with a surprisingly effective performance by Cena at its center, with a fun-to-watch ensemble around him. Aug 25, 2025 Full ReviewRead all reviews

Audience Reviews

View More (330)
Andres G No es mala pero tampoco no es una de las mejores series como dice la critica Rated 3.5 out of 5 stars09/07/25Full Review Jude C Absolute-cinema, it's too bad we can't see peacemaker though Rated 5 out of 5 stars09/07/25Full Review Łukasz S Good Rated 5 out of 5 stars09/06/25Full Review Fernando N Essa segunda temporada está me surpreendendo positivamente, a química dos personagens estão incríveis, cenas de ação perfeitas e as participações surpreendentes Rated 5 out of 5 stars09/06/25Full Review Shan V starting to introduce old characters like theyre somehow important to the DCU, thge comedy puts me off and reminds me of the beginnings of Marvel shows where they started out good then went to crap. Rated 0.5 out of 5 stars09/07/25Full Review Zubair A With Gunn at the helm, the mission continues: flawless action, killer music, and jokes that hit like a helmet-beam. Fabulous doesn't even cover it. Rated 5 out of 5 stars09/05/25Full Review Read all reviews
Peacemaker — Season 2

My Rating

Read MoreRead LessPOST RATING WRITE A REVIEW EDIT REVIEW

Episodes

Episode 1Aired Aug 21, 2025The Ties That GrindWhile Peacemaker attempts to join the Justice Gang, Harcourt struggles to find work, and Economos takes on a challenging new assignment.Details Episode 2Aired Aug 28, 2025A Man Is Only as Good as His BirdAs Economos clashes with his new handler, Peacemaker must deal with the consequences of his actions in the alternate dimension.Details Episode 3Aired Sep 4, 2025Another Rick Up My SleeveA man fights for peace at any cost, no matter how many people he has to kill to get it.Details Episode 4Airs Thu Sep 11Need I Say DoorA man fights for peace at any cost, no matter how many people he has to kill to get it.Details Episode 5Airs Thu Sep 18Back to the SutureA man fights for peace at any cost, no matter how many people he has to kill to get it.Details Episode 6Airs Thu Sep 25Ignorance is ChrisA man fights for peace at any cost, no matter how many people he has to kill to get it.Details Episode 7Airs Thu Oct 2A man fights for peace at any cost, no matter how many people he has to kill to get it.Details Episode 8Airs Thu Oct 9A man fights for peace at any cost, no matter how many people he has to kill to get it.Details
Twisted Metal79% 88% Twisted Metal WatchlistTRAILERfor Twisted MetalComrade Detective85% 88% Comrade Detective WatchlistSaul of the Mole Men17% % Saul of the Mole Men WatchlistSomebody Somewhere100% 93% Somebody Somewhere WatchlistTRAILERfor Somebody SomewhereSouth Side100% 92% South Side WatchlistTRAILERfor South SideDiscover more movies and TV shows.View More

Season Info

Executive Producer
James Gunn, Peter Safran, John Cena
Screenwriter
James Gunn
Network
HBO Max
Rating
TV-MA
Genre
Comedy, Action
Original Language
English
Release Date
Aug 21, 2025
+Peacemaker: Season 2 | Rotten Tomatoes