gopro-plus 0.3.0.1 → 0.3.1.0
raw patch · 3 files changed
+155/−78 lines, 3 filesdep +raw-strings-qqPVP ok
version bump matches the API change (PVP)
Dependencies added: raw-strings-qq
API changes (from Hackage documentation)
+ GoPro.Plus.Media: Moment :: Text -> Maybe Int -> Moment
+ GoPro.Plus.Media: [_moment_id] :: Moment -> Text
+ GoPro.Plus.Media: [_moment_time] :: Moment -> Maybe Int
+ GoPro.Plus.Media: data Moment
+ GoPro.Plus.Media: instance Data.Aeson.Types.FromJSON.FromJSON GoPro.Plus.Media.Moment
+ GoPro.Plus.Media: instance Data.Aeson.Types.FromJSON.FromJSON GoPro.Plus.Media.Moments
+ GoPro.Plus.Media: instance Data.Aeson.Types.ToJSON.ToJSON GoPro.Plus.Media.Moment
+ GoPro.Plus.Media: instance GHC.Classes.Eq GoPro.Plus.Media.PageInfo
+ GoPro.Plus.Media: instance GHC.Generics.Generic GoPro.Plus.Media.Moment
+ GoPro.Plus.Media: instance GHC.Show.Show GoPro.Plus.Media.Moment
+ GoPro.Plus.Media: moment_id :: Lens' Moment Text
+ GoPro.Plus.Media: moment_time :: Lens' Moment (Maybe Int)
+ GoPro.Plus.Media: moments :: (HasGoProAuth m, MonadIO m) => MediumID -> m [Moment]
Files
- gopro-plus.cabal +4/−3
- src/GoPro/Plus/Media.hs +126/−74
- test/Spec.hs +25/−1
gopro-plus.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: aad6d39dfff268773a2546434545e5c4ef4a88e542724369091bd0a343e5f2e6+-- hash: 127fa4201fe98756bd46ca02b48f05168aac1d43f403d89bac1e8c5997f147a5 name: gopro-plus-version: 0.3.0.1+version: 0.3.1.0 synopsis: GoPro Plus Client API. description: Please see the README on GitHub at <https://github.com/dustin/gopro-plus#readme> category: Web@@ -86,6 +86,7 @@ , lens-aeson , mtl , random+ , raw-strings-qq , retry , tasty , tasty-hunit
src/GoPro/Plus/Media.hs view
@@ -36,6 +36,7 @@ FileStuff(..), files, variations, sprites, sidecar_files, FileInfo(..), fileStuff, filename, Error(..), error_reason, error_code, error_description, error_id,+ Moment(..), moment_id, moment_time, moments, -- * Low-level Junk putMedium ) where@@ -66,12 +67,13 @@ import GoPro.Plus.Internal.AuthHTTP import GoPro.Plus.Internal.HTTP -data PageInfo = PageInfo {- _current_page :: Int,- _per_page :: Int,- _total_items :: Int,- _total_pages :: Int- } deriving (Generic, Show)+data PageInfo = PageInfo+ { _current_page :: Int+ , _per_page :: Int+ , _total_items :: Int+ , _total_pages :: Int+ }+ deriving (Generic, Show, Eq) makeLenses ''PageInfo @@ -80,8 +82,12 @@ type MediumID = T.Text -data MediumType = Photo | Video | TimeLapse | TimeLapseVideo | Burst- deriving (Show, Read, Eq)+data MediumType = Photo+ | Video+ | TimeLapse+ | TimeLapseVideo+ | Burst+ deriving (Show, Read, Eq) instance ToJSON MediumType where toJSON = J.String . T.pack . show@@ -90,9 +96,14 @@ parseJSON (J.String x) = pure . read . T.unpack $ x parseJSON invalid = typeMismatch "Response" invalid -data ReadyToViewType = ViewReady | ViewFailure | ViewLoading- | ViewRegistered | ViewTranscoding | ViewProcessing | ViewUploading- deriving (Show, Read, Eq)+data ReadyToViewType = ViewReady+ | ViewFailure+ | ViewLoading+ | ViewRegistered+ | ViewTranscoding+ | ViewProcessing+ | ViewUploading+ deriving (Show, Read, Eq) instance ToJSON ReadyToViewType where toJSON = J.String . T.pack . fmap toLower . drop 4 . show@@ -103,20 +114,21 @@ trans [] = error "empty ready to view type" parseJSON invalid = typeMismatch "Response" invalid -data Medium = Medium {- _medium_id :: MediumID,- _medium_camera_model :: Maybe String,- _medium_captured_at :: UTCTime,- _medium_created_at :: UTCTime,- _medium_file_size :: Maybe Int,- _medium_moments_count :: Int,- _medium_ready_to_view :: ReadyToViewType,- _medium_source_duration :: Maybe String,- _medium_type :: MediumType,- _medium_token :: String,- _medium_width :: Maybe Int,- _medium_height :: Maybe Int- } deriving (Generic, Show)+data Medium = Medium+ { _medium_id :: MediumID+ , _medium_camera_model :: Maybe String+ , _medium_captured_at :: UTCTime+ , _medium_created_at :: UTCTime+ , _medium_file_size :: Maybe Int+ , _medium_moments_count :: Int+ , _medium_ready_to_view :: ReadyToViewType+ , _medium_source_duration :: Maybe String+ , _medium_type :: MediumType+ , _medium_token :: String+ , _medium_width :: Maybe Int+ , _medium_height :: Maybe Int+ }+ deriving (Generic, Show) makeLenses ''Medium @@ -143,10 +155,11 @@ n <- liftIO $ getStdRandom (randomR (1,4)) proxyAuth (thumbnailURL n m) -data Listing = Listing {- _media :: [Medium],- _pages :: PageInfo- } deriving (Generic, Show)+data Listing = Listing+ { _media :: [Medium]+ , _pages :: PageInfo+ }+ deriving (Generic, Show) makeLenses ''Listing @@ -181,14 +194,15 @@ else pure m' -data File = File {- _file_camera_position :: String,- _file_height :: Int,- _file_width :: Int,- _file_item_number :: Int,- _file_orientation :: Int,- _file_url :: String- } deriving (Generic, Show)+data File = File+ { _file_camera_position :: String+ , _file_height :: Int+ , _file_width :: Int+ , _file_item_number :: Int+ , _file_orientation :: Int+ , _file_url :: String+ }+ deriving (Generic, Show) makeLenses ''File @@ -197,14 +211,15 @@ fieldLabelModifier = dropPrefix "_file_" } -data Variation = Variation {- _var_height :: Int,- _var_width :: Int,- _var_label :: String,- _var_quality :: String,- _var_type :: String,- _var_url :: String- } deriving(Generic, Show)+data Variation = Variation+ { _var_height :: Int+ , _var_width :: Int+ , _var_label :: String+ , _var_quality :: String+ , _var_type :: String+ , _var_url :: String+ }+ deriving (Generic, Show) makeLenses ''Variation @@ -213,11 +228,12 @@ fieldLabelModifier = dropPrefix "_var_" } -data SpriteFrame = SpriteFrame {- _frame_count :: Int,- _frame_height :: Int,- _frame_width :: Int- } deriving(Generic, Show)+data SpriteFrame = SpriteFrame+ { _frame_count :: Int+ , _frame_height :: Int+ , _frame_width :: Int+ }+ deriving (Generic, Show) makeLenses ''SpriteFrame @@ -226,14 +242,15 @@ fieldLabelModifier = dropPrefix "_frame_" } -data Sprite = Sprite {- _sprite_fps :: Double,- _sprite_frame :: SpriteFrame,- _sprite_height :: Int,- _sprite_width :: Int,- _sprite_type :: String,- _sprite_urls :: [String]- } deriving (Generic, Show)+data Sprite = Sprite+ { _sprite_fps :: Double+ , _sprite_frame :: SpriteFrame+ , _sprite_height :: Int+ , _sprite_width :: Int+ , _sprite_type :: String+ , _sprite_urls :: [String]+ }+ deriving (Generic, Show) makeLenses ''Sprite @@ -242,22 +259,24 @@ fieldLabelModifier = dropPrefix "_sprite_" } -data FileStuff = FileStuff {- _files :: [File],- _variations :: [Variation],- _sprites :: [Sprite],- _sidecar_files :: [Value]- } deriving (Generic, Show)+data FileStuff = FileStuff+ { _files :: [File]+ , _variations :: [Variation]+ , _sprites :: [Sprite]+ , _sidecar_files :: [Value]+ }+ deriving (Generic, Show) makeLenses ''FileStuff instance FromJSON FileStuff where parseJSON = genericParseJSON jsonOpts -data FileInfo = FileInfo {- _fileStuff :: FileStuff,- _filename :: String- } deriving (Generic, Show)+data FileInfo = FileInfo+ { _fileStuff :: FileStuff+ , _filename :: String+ }+ deriving (Generic, Show) makeLenses ''FileInfo @@ -277,12 +296,13 @@ retrieve :: (HasGoProAuth m, FromJSON j, MonadIO m) => MediumID -> m j retrieve k = jgetAuth (dlURL k) -data Error = Error {- _error_reason :: String,- _error_code :: Int,- _error_description :: String,- _error_id :: String- } deriving (Generic, Show)+data Error = Error+ { _error_reason :: String+ , _error_code :: Int+ , _error_description :: String+ , _error_id :: String+ }+ deriving (Generic, Show) makeLenses ''Error @@ -321,3 +341,35 @@ where v :: Value -> () v = const ()++-- | A moment of interestingness in a Medium.+data Moment = Moment+ { _moment_id :: T.Text+ , _moment_time :: Maybe Int+ }+ deriving (Show, Generic)++makeLenses ''Moment++instance FromJSON Moment where+ parseJSON = genericParseJSON defaultOptions {+ fieldLabelModifier = dropPrefix "_moment_"+ }++instance ToJSON Moment where+ toEncoding = genericToEncoding jsonOpts{ fieldLabelModifier = dropPrefix "_moment_" }+ toJSON = genericToJSON jsonOpts{ fieldLabelModifier = dropPrefix "_moment_" }++newtype Moments = Moments { unMoments :: [Moment] }++instance FromJSON Moments where+ parseJSON (Object v) = do+ o <- v .: "_embedded"+ m <- o .: "moments"+ Moments <$> traverse parseJSON (V.toList m)++ parseJSON invalid = typeMismatch "Response" invalid++-- | Get the moments for the given medium.+moments :: (HasGoProAuth m, MonadIO m) => MediumID -> m [Moment]+moments mid = unMoments <$> jgetAuth ("https://api.gopro.com/media/" <> T.unpack mid <> "/moments?fields=time")
test/Spec.hs view
@@ -1,10 +1,34 @@+{-# LANGUAGE QuasiQuotes #-}++import Control.Lens+import qualified Data.Aeson as J+import qualified Data.ByteString.Lazy as BL+import Text.RawString.QQ (r)+ import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.QuickCheck as QC +import GoPro.Plus.Media++exampleMedia :: BL.ByteString+exampleMedia = [r|+{"_pages":{"current_page":1,"per_page":100,"total_items":2169,"total_pages":22},+"_embedded":{"errors":[],"media":[+{"captured_at":"2020-04-06T15:51:32Z","content_title":"GOPR1991.MP4","content_type":null,"created_at":"2020-04-07T03:35:27Z","gopro_user_id":"uid","file_size":412808724,"height":1080,"fov":null,"id":"62NzWRrpPXm7o","moments_count":0,"on_public_profile":false,"orientation":1,"play_as":"video","ready_to_edit":true,"ready_to_view":"ready","resolution":"1080p","source_duration":"109710","token":"token1","type":"Video","width":1920},+{"captured_at":"2020-04-06T00:33:22Z","content_title":null,"content_type":null,"created_at":"2020-04-06T01:39:39Z","gopro_user_id":"uid","file_size":7930021,"height":3000,"fov":null,"id":"EDavpqr4GVe8O","moments_count":0,"on_public_profile":false,"orientation":1,"play_as":"photo","ready_to_edit":true,"ready_to_view":"ready","resolution":"12000000","source_duration":"0","token":"token2","type":"Photo","width":4000}]}}+|]++testSearchParser :: Assertion+testSearchParser = do+ let l = J.decode exampleMedia :: Maybe Listing+ assertEqual (show l) (Just (PageInfo 1 100 2169 22)) (l ^? _Just . pages)+ assertEqual (show l) [Video, Photo] (l ^.. _Just . media . folded . medium_type)+ assertEqual (show l) ["62NzWRrpPXm7o", "EDavpqr4GVe8O"] (l ^.. _Just . media . folded . medium_id)+ tests :: [TestTree] tests = [- testProperty "a test property" (\x -> (x::Int) == id x)+ testCase "Parsing" testSearchParser ] main :: IO ()