diff --git a/elm.json b/elm.json index 539206c5..9db5be60 100644 --- a/elm.json +++ b/elm.json @@ -28,6 +28,7 @@ "elm-explorations/markdown": "1.0.0", "icidasset/elm-material-icons": "7.0.0", "mdgriffith/elm-ui": "1.1.8", + "myrho/elm-round": "1.0.4", "phollyer/elm-ui-dropdown": "2.2.2", "ryannhg/elm-spa": "6.0.0" }, @@ -43,7 +44,6 @@ "fredcy/elm-parseint": "2.0.1", "miniBill/elm-ui-with-context": "1.1.0", "mpizenberg/elm-pointer-events": "4.0.2", - "myrho/elm-round": "1.0.4", "noahzgordon/elm-color-extra": "1.0.2", "phollyer/elm-cursor": "1.1.1", "pzp1997/assoc-list": "1.0.0", diff --git a/public/main.css b/public/main.css index 9a199be9..fc460e6e 100644 --- a/public/main.css +++ b/public/main.css @@ -285,6 +285,9 @@ h2 { width: 100%; height: auto; } +.card-parent { + margin: 20px 0 0 20px !important; +} .card-parent:hover > .card { opacity: 1; transition: opacity 0.3s; @@ -319,20 +322,18 @@ h2 { position: absolute !important; cursor: pointer; } -.image-gradient { - position: absolute !important; -} -.image-gradient > div > img { +.image-gradient img { -webkit-mask-image: linear-gradient( 90deg, rgba(2, 0, 36, 0) 10%, rgba(9, 9, 121, 1) 130% ) !important; mask-image: linear-gradient( - to right, + to left, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0) ) !important; + height: 100%; } .video-modal { z-index: 3; @@ -356,6 +357,23 @@ h2 { filter: grayscale(0%); } +#description { + display: none; +} + +#description:checked + label { + height: 100%; +} +.description-text { + display: block; + word-break: break-word; + white-space: normal; + overflow: hidden; + height: 22px; + line-height: 1.5em; + cursor: pointer; +} + @keyframes popup { 0% { transform: scale(5) rotate(0); diff --git a/src/Components/SectionHeader.elm b/src/Components/SectionHeader.elm index fb515920..20720be4 100644 --- a/src/Components/SectionHeader.elm +++ b/src/Components/SectionHeader.elm @@ -1,4 +1,4 @@ -module Components.SectionHeader exposing (view, viewAlbums, viewArtists, viewMovies, viewTvShows, viewVideos) +module Components.SectionHeader exposing (view, viewAlbums, viewArtists, viewMovies, viewSeasons, viewTvShows, viewVideos) import Colors exposing (cardHover) import Element as Element exposing (Attribute, Element, alignBottom, alignLeft, alignRight, alignTop, centerX, centerY, clipX, column, el, fill, fillPortion, height, image, maximum, minimum, mouseOver, padding, paddingEach, paddingXY, px, rgb, row, spacingXY, width, wrappedRow) @@ -10,10 +10,11 @@ import Helper exposing (durationToString) import Html.Attributes import Material.Icons as Filled import Material.Icons.Types as MITypes exposing (Icon) +import Round import Spa.Generated.Route as Route exposing (Route) import Url exposing (percentDecode, percentEncode) import Url.Builder exposing (crossOrigin) -import WSDecoder exposing (AlbumObj, ArtistObj, ItemDetails, MovieObj, SongObj, TvshowObj, VideoObj) +import WSDecoder exposing (AlbumObj, ArtistObj, ItemDetails, MovieObj, SeasonObj, SongObj, TvshowObj, VideoObj) view : String -> Maybe msg -> Bool -> List { title : String, action : Maybe msg } -> Element msg @@ -276,12 +277,52 @@ viewTvShows buttonMsg tvshow = , el [ alignBottom, padding 10 ] (materialButtonBig ( Filled.play_arrow, buttonMsg )) ] , Element.link [ Element.width fill, Element.height fill, alignBottom, paddingEach { left = 10, right = 0, top = 10, bottom = 10 }, Font.color Colors.black ] - { url = "" -- TODO : direct the link towards the individual tvshow page once the routing has been implemented + { url = Route.toString (Route.Tvshows__Tvshowid_Int { tvshowid = tvshow.tvshowid }) , label = column [] [ Element.text tvshow.label , el [ paddingEach { left = 0, right = 0, top = 5, bottom = 0 }, Font.color Colors.greyscaleGray, Font.size 13 ] - (Element.text (String.slice 0 3 (String.fromFloat tvshow.rating))) + (Element.text (Round.round 1 tvshow.rating)) + ] + } + ] + + +viewSeasons : msg -> SeasonObj -> Element msg +viewSeasons buttonMsg season = + column + [ Background.color (rgb 1 1 1) + , Element.htmlAttribute (Html.Attributes.class "card-parent") + , Element.height (fill |> maximum 310) + , Element.width (fill |> minimum 170 |> maximum 170) + , Element.htmlAttribute (Html.Attributes.style "box-shadow" "0px 0px 2px #888888") + ] + [ case season.poster of + "" -> + image [ alignTop, width fill, height fill ] + { src = "/thumbnail_default.png" + , description = "Default thumbnail" + } + + _ -> + image [ alignTop, width fill, height fill ] + { src = crossOrigin "http://localhost:8080" [ "image", percentEncode season.poster ] [] + , description = "Poster" + } + , column [ Element.htmlAttribute (Html.Attributes.class "card"), Element.height (px 255), Element.width (fill |> minimum 170 |> maximum 170), Background.color cardHover ] + [ row [ alignTop, alignRight, paddingXY 0 10 ] + [ materialButton ( Filled.check_box_outline_blank, buttonMsg ) -- TODO : checkbox to Set Watched + , materialButtonBig ( Filled.more_vert, buttonMsg ) -- TODO : Add Dropdown + ] + , el [ alignBottom, padding 10 ] (materialButtonBig ( Filled.play_arrow, buttonMsg )) -- TODO: make it functional once EpisodeObjs have been created + ] + , Element.link [ Element.width fill, Element.height fill, alignBottom, paddingEach { left = 10, right = 0, top = 10, bottom = 10 }, Font.color Colors.black ] + { url = "" --- TODO : redirect url to seasonid_int page when it gets implemented + , label = + column [] + [ Element.text season.label + , el [ paddingEach { left = 0, right = 0, top = 5, bottom = 0 }, Font.color Colors.greyscaleGray, Font.size 13 ] + (Element.text (String.fromInt season.episode ++ " episodes")) ] } ] diff --git a/src/Pages/Music/Album/Albumid_Int.elm b/src/Pages/Music/Album/Albumid_Int.elm index 101317ba..5d9b2eea 100644 --- a/src/Pages/Music/Album/Albumid_Int.elm +++ b/src/Pages/Music/Album/Albumid_Int.elm @@ -151,8 +151,8 @@ view model = ] Just album -> - column [ Element.height fill, Element.width fill, Background.color Colors.sidebar ] - [ row [ Element.height (fillPortion 1), Element.width fill, Background.color (Element.rgba255 50 53 55 1), Element.htmlAttribute (Html.Attributes.class "card-parent"), padding 30 ] + column [ Element.height fill, Element.width fill ] + [ row [ Element.height (fill |> maximum 300), Element.width fill, Background.color (Element.rgba255 50 53 55 1) ] [ column [ Element.width (px 250), Element.height (px 250), Element.htmlAttribute (Html.Attributes.class "card-parent") ] [ case album.thumbnail of "" -> @@ -181,7 +181,7 @@ view model = ) ] ] - , column [ alignTop, Element.height fill, Element.width (fillPortion 7), paddingXY 10 25 ] + , column [ Element.width fill, paddingXY 10 25 ] [ row [] [ el [ Font.color white, Font.size 30 ] (Element.text album.label), el [ alignBottom, paddingXY 10 0, Font.size 15 ] (Element.text (String.fromInt album.year)) ] , column [ paddingXY 0 35, spacingXY 0 10, Font.size 14 ] [ row [] @@ -224,8 +224,20 @@ view model = } ] ] + , case album.fanart of + "" -> + image [ Element.width fill, Element.height fill, Element.htmlAttribute (Html.Attributes.class "image-gradient") ] + { src = "/concert.jpg" + , description = "Default" + } + + _ -> + image [ Element.width fill, Element.height fill, Element.htmlAttribute (Html.Attributes.class "image-gradient") ] + { src = crossOrigin "http://localhost:8080" [ "image", percentEncode album.fanart ] [] + , description = "Fanart" + } ] - , column [ Element.height (fillPortion 5), Element.width fill, paddingXY 15 25, spacingXY 5 7 ] + , column [ Element.height fill, Element.width fill, paddingXY 15 25, spacingXY 5 7, Background.color (Element.rgba255 245 245 245 1) ] (List.map (\song -> el [ Element.width (fill |> maximum 1200), paddingEach { left = 10, top = 0, right = 60, bottom = 0 } ] @@ -255,17 +267,6 @@ view model = ) model.song_list ) - , case album.fanart of - "" -> - Element.none - - _ -> - column [ Element.htmlAttribute (Html.Attributes.class "image-gradient"), alignRight, alignTop ] - [ image [ Element.width (fillPortion 2 |> maximum 550) ] - { src = crossOrigin "http://localhost:8080" [ "image", percentEncode album.fanart ] [] - , description = "Fanart" - } - ] ] ] } diff --git a/src/Pages/Music/Videos/Videoid_Int.elm b/src/Pages/Music/Videos/Videoid_Int.elm index 4a4316d2..b5119de9 100644 --- a/src/Pages/Music/Videos/Videoid_Int.elm +++ b/src/Pages/Music/Videos/Videoid_Int.elm @@ -159,8 +159,8 @@ view model = ] Just video -> - column [ Element.height fill, Element.width fill, Background.color Colors.sidebar ] - [ row [ Element.height (fillPortion 1), Element.width fill, Background.color (Element.rgba255 50 53 55 1), Element.htmlAttribute (Html.Attributes.class "card-parent"), paddingXY 20 10 ] + column [ Element.height fill, Element.width fill ] + [ row [ Element.height (fill |> maximum 300), Element.width fill, Background.color (Element.rgba255 50 53 55 1) ] [ column [ Element.width (px 250), Element.height (px 250), Element.htmlAttribute (Html.Attributes.class "card-parent") ] [ case video.thumbnail of "" -> @@ -189,7 +189,7 @@ view model = ) ] ] - , column [ alignTop, Element.height fill, Element.width (fillPortion 7), paddingXY 10 35 ] + , column [ Element.height fill, Element.width fill, paddingXY 10 35 ] [ el [ Font.color white, Font.size 30 ] (Element.text video.label) , column [ paddingEach { top = 30, left = 0, right = 0, bottom = 0 }, spacingXY 0 12, Font.size 14 ] [ row [] @@ -237,16 +237,14 @@ view model = } ] ] - ] - , column [ Element.height (fillPortion 5), Element.width fill, paddingXY 35 35, spacingXY 5 7 ] - [ el [ Font.size 30, Font.color black ] (Element.text "Related music videos from YouTube") - ] - , column [ Element.htmlAttribute (Html.Attributes.class "image-gradient"), alignRight, alignTop ] - [ image [ Element.width (fillPortion 2 |> maximum 540) ] + , image [ Element.width fill, Element.height fill, Element.htmlAttribute (Html.Attributes.class "image-gradient") ] { src = "/concert.jpg" , description = "Fanart" } ] + , column [ Element.height fill, Element.width fill, paddingXY 35 35, spacingXY 5 7 ] + [ el [ Font.size 30, Font.color black ] (Element.text "Related music videos from YouTube") + ] , case model.modalstate of Open -> column [ Element.htmlAttribute (Html.Attributes.class "video-modal"), Element.width fill, Element.height fill ] diff --git a/src/Pages/Tvshows/Recent.elm b/src/Pages/Tvshows/Recent.elm index c6e91526..b01ef286 100644 --- a/src/Pages/Tvshows/Recent.elm +++ b/src/Pages/Tvshows/Recent.elm @@ -61,7 +61,7 @@ type alias Model = init : Shared.Model -> Url Params -> ( Model, Cmd Msg ) init shared url = ( { currentlyPlaying = shared.currentlyPlaying, tvshow_list = shared.tvshow_list, route = url.route } - , sendAction """{"jsonrpc": "2.0", "method": "VideoLibrary.GetTvshows", "params": { "filter": {"field": "playcount", "operator": "is", "value": "0"}, "properties" : ["art", "rating", "thumbnail", "playcount", "file","year","dateadded"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libTvshows"}""" + , sendAction """{"jsonrpc": "2.0", "method": "VideoLibrary.GetTvshows", "params": { "filter": {"field": "playcount", "operator": "is", "value": "0"}, "properties" : ["art", "rating", "thumbnail", "playcount", "file","year","dateadded","mpaa","genre","season","studio","episode","watchedepisodes","plot","cast"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libTvshows"}""" ) diff --git a/src/Pages/Tvshows/Tvshowid_Int.elm b/src/Pages/Tvshows/Tvshowid_Int.elm new file mode 100644 index 00000000..ca7e89ac --- /dev/null +++ b/src/Pages/Tvshows/Tvshowid_Int.elm @@ -0,0 +1,218 @@ +module Pages.Tvshows.Tvshowid_Int exposing (Model, Msg, Params, page) + +import Colors exposing (black, cardHover, darkGreyIcon, greyIcon, white, whiteIcon) +import Components.SectionHeader +import Element exposing (..) +import Element.Background as Background +import Element.Font as Font exposing (Font) +import Element.Input as Input +import Html exposing (Html, div, img, input, label, p) +import Html.Attributes exposing (..) +import Material.Icons as Filled +import Material.Icons.Types as MITypes exposing (Icon) +import Request +import Round +import Shared exposing (sendActions) +import Spa.Document exposing (Document) +import Spa.Generated.Route as Route exposing (Route) +import Spa.Page as Page exposing (Page) +import Spa.Url as Url exposing (Url) +import Url exposing (percentEncode) +import Url.Builder exposing (crossOrigin) +import WSDecoder exposing (ArtistObj, SeasonObj, TvshowObj, VideoObj) + + +page : Page Params Model Msg +page = + Page.application + { init = init + , update = update + , subscriptions = subscriptions + , view = view + , save = save + , load = load + } + + +type Msg + = ReplaceMe + + +type alias Params = + { tvshowid : Int } + + +type alias Model = + { tvshowid : Int + , tvshow : Maybe TvshowObj + , tvshow_list : List TvshowObj + , season_list : List SeasonObj + } + + +init : Shared.Model -> Url Params -> ( Model, Cmd Msg ) +init shared { params } = + ( { tvshowid = params.tvshowid, tvshow = getTvShow params.tvshowid shared.tvshow_list, tvshow_list = shared.tvshow_list, season_list = shared.season_list } + , sendActions [ """{"jsonrpc": "2.0", "method": "VideoLibrary.GetSeasons", "params": {"tvshowid": """ ++ String.fromInt params.tvshowid ++ """ ,"properties":["season","episode","tvshowid","art"]}, "id": "libSeasons"}""" ] + ) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + ReplaceMe -> + ( model, Cmd.none ) + + +save : Model -> Shared.Model -> Shared.Model +save model shared = + { shared | season_list = model.season_list } + + +load : Shared.Model -> Model -> ( Model, Cmd Msg ) +load shared model = + ( { model | season_list = shared.season_list }, Cmd.none ) + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + +getTvShow : Int -> List TvshowObj -> Maybe TvshowObj +getTvShow id tvShowlist = + List.head (List.filter (\tvshow -> id == tvshow.tvshowid) tvShowlist) + + + +-- VIEWS + + +view : Model -> Document Msg +view model = + { title = "TvShow_Int" + , body = + [ case model.tvshow of + Nothing -> + column [ Element.height fill, Element.width fill ] + [ Element.text (String.fromInt model.tvshowid) + ] + + Just tvshow -> + column [ Element.height fill, Element.width fill ] + [ row [ Element.height fill, Element.width fill, Background.color (Element.rgba255 50 53 51 1) ] + [ column [ Element.width (px 250), Element.height (px 250), Element.htmlAttribute (Html.Attributes.class "card-parent"), alignTop ] + [ case tvshow.thumbnail of + "" -> + image [ Element.height (fill |> maximum 360), Element.width (fillPortion 2 |> maximum 240) ] + { src = "/thumbnail_default.png" + , description = "Default Thumbnail" + } + + _ -> + image [ Element.height (fill |> maximum 360), Element.width (fillPortion 2 |> maximum 240) ] + { src = crossOrigin "http://localhost:8080" [ "image", percentEncode tvshow.thumbnail ] [] + , description = "Thumbnail" + } + , column [ Element.htmlAttribute (Html.Attributes.class "card"), Element.height (px 360), Element.width (fill |> minimum 230 |> maximum 240), Background.color cardHover ] + [ row [ alignTop, alignRight, paddingXY 15 15 ] + [ Input.button [] + { onPress = Nothing + , label = Element.html (Filled.thumb_up 25 (MITypes.Color <| Colors.whiteIcon)) + } + ] + , el [ htmlAttribute (Html.Attributes.style "margin" "auto"), paddingEach { top = 0, left = 0, right = 0, bottom = 50 } ] + (Input.button [] + { onPress = Nothing + , label = Element.html (Filled.play_arrow 45 (MITypes.Color <| Colors.whiteIcon)) + } + ) + ] + ] + , column [ alignTop, Element.height fill, Element.width (fillPortion 7 |> maximum 900), paddingXY 10 35 ] + [ row [ alignRight, alignTop, Font.size 25, Element.htmlAttribute (Html.Attributes.style "position" "absolute") ] [ Element.text (Round.round 1 tvshow.rating), Element.html (Filled.star 36 (MITypes.Color <| greyIcon)) ] + , row [] [ el [ Font.color white, Font.size 30 ] (Element.text tvshow.label), el [ alignBottom, paddingXY 10 0, Font.size 15 ] (Element.text (String.fromInt tvshow.year)) ] + , column [ paddingEach { top = 20, left = 0, right = 0, bottom = 0 }, spacingXY 0 12, Font.size 14 ] + [ row [] + [ el [ Font.color white ] (Element.text "Genre: ") + , row [] + (List.map + (\genre -> + Element.text (genre ++ " ") + ) + tvshow.genre + ) + ] + , row [] + [ el [ Font.color white ] (Element.text "Cast: ") + , row [] + (List.map + (\cast -> + Element.text (cast.name ++ ", ") + ) + (List.take 5 tvshow.cast) + ) + ] + , row [] + [ el [ Font.color white ] (Element.text "Studio: ") + , row [] + (List.map + (\studio -> + Element.text (studio ++ " ") + ) + tvshow.studio + ) + ] + , row [] [ el [ Font.color white ] (Element.text "Rated: "), Element.text tvshow.mpaa ] + , row [] [ el [ Font.color white ] (Element.text "Episodes: "), Element.text (String.fromInt tvshow.episode ++ " total" ++ " " ++ "(" ++ String.fromInt (tvshow.episode - tvshow.watchedpisode) ++ " unwatched)") ] + , Element.html + (div [ style "margin" "2em 0em" ] + [ input [ type_ "checkbox", id "description" ] [] + , label [ class "description-text", for "description" ] [ Html.text tvshow.plot ] + ] + ) + ] + , row [ spacingXY 10 0 ] + [ Input.button [ paddingXY 12 8, Background.color Colors.navTextHover ] + { onPress = Nothing -- TODO : make it functional once EpisodeObj have been created + , label = row [] [ el [ Font.color white, paddingEach { top = 0, left = 0, right = 10, bottom = 0 } ] (Element.text "Play"), Element.html (Filled.play_circle_filled 16 (MITypes.Color <| whiteIcon)) ] + } + , Input.button [ paddingXY 12 8, Background.color (Element.rgba255 71 74 75 1) ] + { onPress = Nothing -- TODO : make it functional once EpisodeObj have been created + , label = row [] [ el [ Font.color white, paddingEach { top = 0, left = 0, right = 10, bottom = 0 } ] (Element.text "Queue"), Element.html (Filled.add_circle 16 (MITypes.Color <| greyIcon)) ] + } + , Input.button [ paddingXY 12 8, Background.color (Element.rgba255 71 74 75 1) ] + { onPress = Nothing -- TODO : Button to set Watched + , label = row [] [ el [ Font.color white, paddingEach { top = 0, left = 0, right = 10, bottom = 0 } ] (Element.text "Set Watched"), Element.html (Filled.check_box 16 (MITypes.Color <| greyIcon)) ] + } + , Input.button [ paddingXY 12 8, Background.color (Element.rgba255 71 74 75 1) ] + { onPress = Nothing -- TODO : Add More menu dropdown + , label = row [] [ el [ Font.color white, paddingEach { top = 0, left = 0, right = 10, bottom = 0 } ] (Element.text "More"), Element.html (Filled.more_vert 16 (MITypes.Color <| greyIcon)) ] + } + ] + ] + , case tvshow.fanart of + "" -> + image [ Element.width fill, Element.height fill, Element.htmlAttribute (Html.Attributes.class "image-gradient") ] + { src = "/concert.jpg" + , description = "Default-Fanart" + } + + _ -> + image [ Element.width fill, Element.height fill, Element.htmlAttribute (Html.Attributes.class "image-gradient") ] + { src = crossOrigin "http://localhost:8080" [ "image", percentEncode tvshow.fanart ] [] + , description = "Fanart" + } + ] + , column [ Element.height fill, Element.width fill, paddingXY 35 35, spacingXY 5 7, Background.color (Element.rgba255 245 245 245 1) ] + [ wrappedRow [ spacingXY 15 0 ] + (List.map + (\season -> + Components.SectionHeader.viewSeasons ReplaceMe season + ) + model.season_list + ) + ] + ] + ] + } diff --git a/src/Pages/Videoplayer/Movieid_Int.elm b/src/Pages/Videoplayer/Movieid_Int.elm index 061dc7d2..3a481b37 100644 --- a/src/Pages/Videoplayer/Movieid_Int.elm +++ b/src/Pages/Videoplayer/Movieid_Int.elm @@ -180,8 +180,8 @@ view model = ] Just movie -> - column [ Element.height fill, Element.width fill, Background.color Colors.sidebar ] - [ row [ Element.height (fillPortion 1), Element.width fill, Background.color (Element.rgba255 50 53 55 1), Element.htmlAttribute (Html.Attributes.class "card-parent"), paddingXY 20 15 ] + column [ Element.height fill, Element.width fill ] + [ row [ Element.height fill, Element.width fill, Background.color (Element.rgba255 50 53 55 1), paddingXY 20 0 ] [ column [ Element.width (px 250), Element.height (px 250), Element.htmlAttribute (Html.Attributes.class "card-parent"), alignTop ] [ case movie.thumbnail of "" -> @@ -210,11 +210,11 @@ view model = ) ] ] - , column [ alignTop, Element.height fill, Element.width (fillPortion 7 |> maximum 900), paddingXY 10 35 ] + , column [ alignTop, Element.height fill, Element.width fill, paddingXY 10 35 ] [ row [ alignRight, alignTop, Font.size 25, Element.htmlAttribute (Html.Attributes.style "position" "absolute") ] [ Element.text (String.slice 0 3 (String.fromFloat movie.rating)), Element.html (Filled.star 36 (MITypes.Color <| greyIcon)) ] , row [] [ el [ Font.color white, Font.size 30 ] (Element.text movie.label), el [ alignBottom, paddingXY 10 0, Font.size 15 ] (Element.text (String.fromInt movie.year)) ] , el [ Font.size 19, paddingEach { top = 20, left = 0, right = 0, bottom = 0 } ] (movie.runtime |> durationToString |> text) - , column [ paddingEach { top = 20, left = 0, right = 0, bottom = 0 }, spacingXY 0 12, Font.size 14 ] + , column [ paddingEach { top = 20, left = 0, right = 0, bottom = 15 }, spacingXY 0 12, Font.size 14 ] [ row [] [ el [ Font.color white ] (Element.text "Genre: ") , row [] @@ -294,8 +294,20 @@ view model = } ] ] + , case movie.fanart of + "" -> + image [ Element.width fill, Element.height fill, Element.htmlAttribute (Html.Attributes.class "image-gradient") ] + { src = "/concert.jpg" + , description = "Fanart" + } + + _ -> + image [ Element.width fill, Element.height fill, Element.htmlAttribute (Html.Attributes.class "image-gradient") ] + { src = crossOrigin "http://localhost:8080" [ "image", percentEncode movie.fanart ] [] + , description = "Fanart" + } ] - , column [ Element.height (fillPortion 5), Element.width fill, paddingXY 35 35, spacingXY 5 7 ] + , column [ Element.height fill, Element.width fill, paddingXY 35 35, spacingXY 5 7 ] [ el [ Font.size 30, Font.color black ] (Element.text "Synopsis") , paragraph [ Element.width (fill |> maximum 950), spacing 10, paddingXY 0 10, Font.color Colors.black ] [ Element.text movie.plot ] , row [ Element.width (fill |> maximum 950), Element.height (px 200), clipX, scrollbarX, spacingXY 10 0 ] @@ -327,22 +339,6 @@ view model = ) ] ] - , case movie.fanart of - "" -> - column [ Element.htmlAttribute (Html.Attributes.class "image-gradient"), alignRight, alignTop ] - [ image [ Element.width (fillPortion 2 |> maximum 740) ] - { src = "/concert.jpg" - , description = "Fanart" - } - ] - - _ -> - column [ Element.htmlAttribute (Html.Attributes.class "image-gradient"), alignRight, alignTop ] - [ image [ Element.width (fillPortion 2 |> maximum 740) ] - { src = crossOrigin "http://localhost:8080" [ "image", percentEncode movie.fanart ] [] - , description = "Fanart" - } - ] , case model.modalstate of Open -> column [ Element.htmlAttribute (Html.Attributes.class "video-modal"), Element.width fill, Element.height fill ] diff --git a/src/Shared.elm b/src/Shared.elm index 87c62c21..320c8be1 100644 --- a/src/Shared.elm +++ b/src/Shared.elm @@ -36,7 +36,7 @@ import Spa.Generated.Route as Route exposing (Route) import Time import Translations import Url exposing (Url) -import WSDecoder exposing (AlbumObj, ArtistObj, Connection(..), FileObj, ItemDetails, LeftSidebarMenuHover(..), LocalPlaylists, LocalSettings, MovieObj, PType(..), ParamsResponse, PlayerObj(..), PlaylistObj, ResultResponse(..), SettingsObj, SongObj, SourceObj, TvshowObj, VideoObj, decodeLocalSettings, getMediaType, localPlaylistDecoder, localPlaylistEncoder, paramsResponseDecoder, resultResponseDecoder) +import WSDecoder exposing (AlbumObj, ArtistObj, Connection(..), FileObj, ItemDetails, LeftSidebarMenuHover(..), LocalPlaylists, LocalSettings, MovieObj, PType(..), ParamsResponse, PlayerObj(..), PlaylistObj, ResultResponse(..), SeasonObj, SettingsObj, SongObj, SourceObj, TvshowObj, VideoObj, decodeLocalSettings, getMediaType, localPlaylistDecoder, localPlaylistEncoder, paramsResponseDecoder, resultResponseDecoder) @@ -92,6 +92,7 @@ type alias Model = , interfaceLocalSettings : LocalSettings , addonLocalSettings : LocalSettings , tabSwitch : SharedType.Tabs + , season_list : List SeasonObj } @@ -291,6 +292,7 @@ init flags url key = , tvshow_list = [] , source_list = [] , file_list = [] + , season_list = [] , volumeSlider = SingleSlider.init { min = 0 @@ -325,7 +327,7 @@ init flags url key = , """{"jsonrpc": "2.0", "method": "AudioLibrary.GetArtists", "params": { "properties": [ "thumbnail", "fanart", "born", "formed", "died", "disbanded", "yearsactive", "mood", "style", "genre" ] }, "id": 1}""" , """{"jsonrpc": "2.0", "method": "VideoLibrary.GetMusicVideos", "params": { "properties": [ "title", "thumbnail", "artist", "album", "genre", "lastplayed", "year", "runtime", "fanart", "file", "streamdetails","dateadded" ] }, "id": "libMusicVideos"}""" , """{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "properties" : ["art", "rating", "thumbnail", "playcount", "file","year","dateadded","genre","director","cast","streamdetails","mpaa","runtime","writer"] }, "id": "libMovies"}""" - , """{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "properties": ["art", "genre", "plot", "title", "originaltitle", "year", "rating", "thumbnail", "playcount", "file", "fanart","dateadded","year"] }, "id": "libTvShows"}""" + , """{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "properties": ["art", "genre", "plot", "title", "originaltitle", "year", "rating", "thumbnail", "playcount", "file", "fanart","dateadded","year","mpaa","genre","season","studio","episode","watchedepisodes","cast"] }, "id": "libTvShows"}""" , """{"jsonrpc": "2.0", "method": "Files.GetSources", "params": { "media": "video" }, "id": 1 }""" --get video and music sources , """{"jsonrpc": "2.0", "method": "Files.GetSources", "params": { "media": "music" }, "id": 1 }""" , """{"jsonrpc": "2.0", "method": "Player.SetShuffle", "params": { "playerid": 0, "shuffle": false }, "id": 1 }""" --set shuffle to false on init @@ -631,6 +633,9 @@ update msg model = ResultN tvshowList -> ( { model | tvshow_list = tvshowList }, Cmd.none ) + ResultO seasonList -> + ( { model | season_list = seasonList }, Cmd.none ) + ToggleRightSidebar -> ( { model | rightSidebarExtended = not model.rightSidebarExtended } , Cmd.none diff --git a/src/Spa/Generated/Pages.elm b/src/Spa/Generated/Pages.elm index 27687327..69ade9c7 100644 --- a/src/Spa/Generated/Pages.elm +++ b/src/Spa/Generated/Pages.elm @@ -51,6 +51,7 @@ import Pages.Thumbsup import Pages.Top import Pages.Tvshows import Pages.Tvshows.Recent +import Pages.Tvshows.Tvshowid_Int import Pages.Videoplayer.Movieid_Int import Shared import Spa.Document as Document exposing (Document) @@ -107,6 +108,7 @@ type Model | Music__Artist__Artistid_Int__Model Pages.Music.Artist.Artistid_Int.Model | Music__Videos__Videoid_Int__Model Pages.Music.Videos.Videoid_Int.Model | Music__Genre__Genre_String__Model Pages.Music.Genre.Genre_String.Model + | Tvshows__Tvshowid_Int__Model Pages.Tvshows.Tvshowid_Int.Model type Msg @@ -153,6 +155,7 @@ type Msg | Music__Artist__Artistid_Int__Msg Pages.Music.Artist.Artistid_Int.Msg | Music__Videos__Videoid_Int__Msg Pages.Music.Videos.Videoid_Int.Msg | Music__Genre__Genre_String__Msg Pages.Music.Genre.Genre_String.Msg + | Tvshows__Tvshowid_Int__Msg Pages.Tvshows.Tvshowid_Int.Msg @@ -291,6 +294,9 @@ init route = Route.Music__Genre__Genre_String params -> pages.music__genre__genre_string.init params + Route.Tvshows__Tvshowid_Int params -> + pages.tvshows__tvshowid_int.init params + -- UPDATE @@ -428,6 +434,9 @@ update bigMsg bigModel = ( Music__Videos__Videoid_Int__Msg msg, Music__Videos__Videoid_Int__Model model ) -> pages.music__videos__videoid_int.update msg model + ( Tvshows__Tvshowid_Int__Msg msg, Tvshows__Tvshowid_Int__Model model ) -> + pages.tvshows__tvshowid_int.update msg model + _ -> ( bigModel, Cmd.none ) @@ -568,6 +577,9 @@ bundle bigModel = Music__Genre__Genre_String__Model model -> pages.music__genre__genre_string.bundle model + Tvshows__Tvshowid_Int__Model model -> + pages.tvshows__tvshowid_int.bundle model + view : Model -> Document Msg view model = @@ -677,6 +689,7 @@ pages : , music__artist__artistid_int : Upgraded Pages.Music.Artist.Artistid_Int.Params Pages.Music.Artist.Artistid_Int.Model Pages.Music.Artist.Artistid_Int.Msg , music__videos__videoid_int : Upgraded Pages.Music.Videos.Videoid_Int.Params Pages.Music.Videos.Videoid_Int.Model Pages.Music.Videos.Videoid_Int.Msg , music__genre__genre_string : Upgraded Pages.Music.Genre.Genre_String.Params Pages.Music.Genre.Genre_String.Model Pages.Music.Genre.Genre_String.Msg + , tvshows__tvshowid_int : Upgraded Pages.Tvshows.Tvshowid_Int.Params Pages.Tvshows.Tvshowid_Int.Model Pages.Tvshows.Tvshowid_Int.Msg } pages = { top = Pages.Top.page |> upgrade Top__Model Top__Msg @@ -722,4 +735,5 @@ pages = , music__artist__artistid_int = Pages.Music.Artist.Artistid_Int.page |> upgrade Music__Artist__Artistid_Int__Model Music__Artist__Artistid_Int__Msg , music__videos__videoid_int = Pages.Music.Videos.Videoid_Int.page |> upgrade Music__Videos__Videoid_Int__Model Music__Videos__Videoid_Int__Msg , music__genre__genre_string = Pages.Music.Genre.Genre_String.page |> upgrade Music__Genre__Genre_String__Model Music__Genre__Genre_String__Msg + , tvshows__tvshowid_int = Pages.Tvshows.Tvshowid_Int.page |> upgrade Tvshows__Tvshowid_Int__Model Tvshows__Tvshowid_Int__Msg } diff --git a/src/Spa/Generated/Route.elm b/src/Spa/Generated/Route.elm index d58b9c36..be89b015 100644 --- a/src/Spa/Generated/Route.elm +++ b/src/Spa/Generated/Route.elm @@ -52,6 +52,7 @@ type Route | Music__Artist__Artistid_Int { artistid : Int } | Music__Videos__Videoid_Int { videoid : Int } | Music__Genre__Genre_String { genre : String } + | Tvshows__Tvshowid_Int { tvshowid : Int } fromUrl : Url -> Maybe Route @@ -119,6 +120,9 @@ routes = , (Parser.s "music" Parser.s "videos" Parser.int) |> Parser.map (\videoid -> { videoid = videoid }) |> Parser.map Music__Videos__Videoid_Int + , (Parser.s "tvshows" Parser.int) + |> Parser.map (\tvshowid -> { tvshowid = tvshowid }) + |> Parser.map Tvshows__Tvshowid_Int ] @@ -256,6 +260,9 @@ toString route = Music__Genre__Genre_String { genre } -> [ "music", "genre", genre ] + + Tvshows__Tvshowid_Int { tvshowid } -> + [ "tvshows", String.fromInt tvshowid ] in segments |> String.join "/" diff --git a/src/WSDecoder.elm b/src/WSDecoder.elm index ca35d8f3..d1526e77 100644 --- a/src/WSDecoder.elm +++ b/src/WSDecoder.elm @@ -1,4 +1,4 @@ -module WSDecoder exposing (AlbumObj, ArtistObj, Connection(..), DefaultElement, FileObj, FileType(..), Item, ItemDetails, LeftSidebarMenuHover(..), LocalPlaylists, LocalSettings, MovieObj, Option, PType(..), ParamsResponse, Path, PlayerObj(..), PlaylistObj, ResultResponse(..), SettingDefault(..), SettingsObj, SongObj, SourceObj, TvshowObj, VideoObj, decodeLocalSettings, encodeLocalSettings, getMediaType, localPlaylistDecoder, localPlaylistEncoder, paramsResponseDecoder, prepareDownloadDecoder, resultResponseDecoder, stringInDefaultElementToString) +module WSDecoder exposing (AlbumObj, ArtistObj, Connection(..), DefaultElement, FileObj, FileType(..), Item, ItemDetails, LeftSidebarMenuHover(..), LocalPlaylists, LocalSettings, MovieObj, Option, PType(..), ParamsResponse, Path, PlayerObj(..), PlaylistObj, ResultResponse(..), SeasonObj, SettingDefault(..), SettingsObj, SongObj, SourceObj, TvshowObj, VideoObj, decodeLocalSettings, encodeLocalSettings, getMediaType, localPlaylistDecoder, localPlaylistEncoder, paramsResponseDecoder, prepareDownloadDecoder, resultResponseDecoder, stringInDefaultElementToString) import Json.Decode as Decode exposing (Decoder, at, bool, float, int, list, nullable, string) import Json.Decode.Pipeline exposing (custom, optional, required) @@ -190,6 +190,7 @@ type ResultResponse | ResultL (List SettingsObj) | ResultM (List VideoObj) | ResultN (List TvshowObj) + | ResultO (List SeasonObj) @@ -273,6 +274,7 @@ queryDecoder = , settingsQueryDecoder , videoQueryDecoder , tvShowQueryDecoder + , seasonQueryDecoder ] @@ -739,6 +741,31 @@ tvShowDecoder = |> required "dateadded" string |> required "rating" Decode.float |> custom (at [ "art", "poster" ] string) + |> required "genre" (list string) + |> required "mpaa" string + |> required "plot" string + |> required "season" int + |> required "studio" (list string) + |> required "episode" int + |> required "watchedepisodes" int + |> required "cast" (list castDecoder) + |> custom (at [ "art", "fanart" ] string) + + +seasonQueryDecoder : Decoder ResultResponse +seasonQueryDecoder = + Decode.succeed ResultO + |> custom (at [ "result", "seasons" ] (list seasonDecoder)) + + +seasonDecoder : Decoder SeasonObj +seasonDecoder = + Decode.succeed SeasonObj + |> required "label" string + |> required "seasonid" int + |> required "episode" int + |> required "tvshowid" int + |> custom (at [ "art", "poster" ] string) type alias MovieObj = @@ -770,6 +797,24 @@ type alias TvshowObj = , dateadded : String , rating : Float , thumbnail : String + , genre : List String + , mpaa : String + , plot : String + , season : Int + , studio : List String + , episode : Int + , watchedpisode : Int + , cast : List CastObj + , fanart : String + } + + +type alias SeasonObj = + { label : String + , seasonid : Int + , episode : Int + , tvshowid : Int + , poster : String }