diff --git a/db/db.go b/db/db.go index 1df5b61..3176585 100644 --- a/db/db.go +++ b/db/db.go @@ -520,3 +520,15 @@ func (c *Client) TmdbIdInWatchlist(tmdb_id int) bool { func (c *Client) GetDownloadHistory(mediaID int) ([]*ent.History, error) { return c.ent.History.Query().Where(history.MediaID(mediaID)).All(context.TODO()) } + +func (c *Client) GetMovieDummyEpisode(movieId int) (*ent.Episode, error) { + _, err := c.ent.Media.Query().Where(media.ID(movieId), media.MediaTypeEQ(media.MediaTypeMovie)).First(context.TODO()) + if err != nil { + return nil, errors.Wrap(err, "get movie") + } + ep, err := c.ent.Episode.Query().Where(episode.MediaID(movieId)).First(context.TODO()) + if err != nil { + return nil, errors.Wrap(err, "query episode") + } + return ep, nil +} \ No newline at end of file diff --git a/ent/schema/episode.go b/ent/schema/episode.go index dda8f58..9f530b9 100644 --- a/ent/schema/episode.go +++ b/ent/schema/episode.go @@ -21,7 +21,6 @@ func (Episode) Fields() []ent.Field { field.String("overview"), field.String("air_date"), field.Enum("status").Values("missing", "downloading", "downloaded").Default("missing"), - field.String("file_in_storage").Optional(), } } diff --git a/server/watchlist.go b/server/watchlist.go index d8ca753..b4e40aa 100644 --- a/server/watchlist.go +++ b/server/watchlist.go @@ -8,6 +8,7 @@ import ( "path/filepath" "polaris/db" "polaris/ent" + "polaris/ent/episode" "polaris/ent/media" "polaris/log" "strconv" @@ -238,6 +239,15 @@ func (s *Server) downloadImage(url string, mediaID int, name string) error { } +type MediaWithStatus struct { + *ent.Media + Status string `json:"status"` +} +//missing: episode aired missing +//downloaded: all monitored episode downloaded +//monitoring: episode aired downloaded, but still has not aired episode +//for movie, only monitoring/downloaded + func (s *Server) GetTvWatchlist(c *gin.Context) (interface{}, error) { list := s.db.GetMediaWatchlist(media.MediaTypeTv) return list, nil @@ -245,7 +255,23 @@ func (s *Server) GetTvWatchlist(c *gin.Context) (interface{}, error) { func (s *Server) GetMovieWatchlist(c *gin.Context) (interface{}, error) { list := s.db.GetMediaWatchlist(media.MediaTypeMovie) - return list, nil + res := make([]MediaWithStatus, len(list)) + for i, item := range list { + var ms = MediaWithStatus{ + Media: item, + Status: "monitoring", + } + dummyEp, err := s.db.GetMovieDummyEpisode(item.ID) + if err != nil { + log.Errorf("get dummy episode: %v", err) + } else { + if dummyEp.Status != episode.StatusMissing { + ms.Status = "downloaded" + } + } + res[i] = ms + } + return res, nil } func (s *Server) GetMediaDetails(c *gin.Context) (interface{}, error) { diff --git a/ui/lib/providers/welcome_data.dart b/ui/lib/providers/welcome_data.dart index f29b26e..b8bd1bf 100644 --- a/ui/lib/providers/welcome_data.dart +++ b/ui/lib/providers/welcome_data.dart @@ -150,6 +150,7 @@ class MediaDetail { String? resolution; int? storageId; String? airDate; + String? status; MediaDetail({ this.id, @@ -163,6 +164,7 @@ class MediaDetail { this.resolution, this.storageId, this.airDate, + this.status, }); MediaDetail.fromJson(Map json) { @@ -177,6 +179,7 @@ class MediaDetail { resolution = json["resolution"]; storageId = json["storage_id"]; airDate = json["air_date"]; + status = json["status"]; } } diff --git a/ui/lib/welcome_page.dart b/ui/lib/welcome_page.dart index 81dea5c..ac82cda 100644 --- a/ui/lib/welcome_page.dart +++ b/ui/lib/welcome_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:quiver/strings.dart'; import 'package:ui/movie_watchlist.dart'; import 'package:ui/providers/APIs.dart'; import 'package:ui/providers/welcome_data.dart'; @@ -27,7 +28,7 @@ class WelcomePage extends ConsumerWidget { return switch (data) { AsyncData(:final value) => SingleChildScrollView( child: Wrap( - spacing: 20, + spacing: 10, children: List.generate(value.length, (i) { var item = value[i]; return Card( @@ -45,16 +46,51 @@ class WelcomePage extends ConsumerWidget { child: Column( children: [ SizedBox( - width: 160, - height: 240, - child: ClipRRect( - borderRadius: BorderRadius.circular(8.0), - child: Image.network( - "${APIs.imagesUrl}/${item.id}/poster.jpg", - fit: BoxFit.fill, - headers: APIs.authHeaders, - ), - )), + width: 130, + height: 195, + child: ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Image.network( + "${APIs.imagesUrl}/${item.id}/poster.jpg", + fit: BoxFit.fill, + headers: APIs.authHeaders, + ), + Padding( + padding: const EdgeInsets.all(10), + child: () { + if (item.mediaType == "tv") { + return Text(""); + } + var icon = const CircleAvatar( + radius: 12, + backgroundColor: Colors.black12, + child: Icon( + Icons.access_time_rounded, + color: Colors.blue, + size: 24, + ), + ); + if (item.status == "downloaded") { + icon = const CircleAvatar( + radius: 12, + backgroundColor: Colors.black12, + child: Icon( + Icons.done_rounded, + color: Colors.green, + size: 24, + ), + ); + } + return icon; + }(), + ) + ], + ), + ), + ), Text( item.name!, style: const TextStyle(