Top100
Your Personal Movie List (C++17 CLI + library)
Loading...
Searching...
No Matches
Movie.h
1// SPDX-License-Identifier: Apache-2.0
2//-------------------------------------------------------------------------------
3// Top100 — Your Personal Movie List
4//
5// File: lib/Movie.h
6// Purpose: Movie model and JSON (de)serialization fields.
7// Language: C++17 (header)
8//
9// Author: Andy McCall, mailme@andymccall.co.uk
10// Date: September 18, 2025
11//-------------------------------------------------------------------------------
12#pragma once
13
14#include <string>
15#include <vector>
16#include <nlohmann/json.hpp>
17
27struct Movie {
28 std::string title;
29 int year;
30 std::string director;
31 // Additional metadata (optional)
32 std::string plotShort; // OMDb short plot
33 std::string plotFull; // OMDb full plot
34 std::vector<std::string> actors; // up to first 10 from OMDb
35 std::vector<std::string> genres; // parsed from comma-separated "Genre"
36 int runtimeMinutes = 0; // parsed from "Runtime" (e.g., "148 min")
37 std::vector<std::string> countries; // parsed from comma-separated "Country"
38 std::string posterUrl; // from "Poster"
39 // Ratings (optional)
40 double imdbRating = 0.0; // 0.0-10.0 from OMDb "imdbRating"
41 int metascore = 0; // 0-100 from OMDb "Metascore"
42 int rottenTomatoes = 0; // 0-100 from OMDb Ratings[Source=="Rotten Tomatoes"]
43 // Provenance and IDs
44 std::string source; // "manual" or "omdb" (optional; default empty)
45 std::string imdbID; // from OMDb; empty for manual entries
46 // User ranking fields
47 double userScore = 1500.0; // Elo-like score for pairwise ranking
48 int userRank = -1; // 1-based rank; -1 means unranked
49};
50
57inline void to_json(nlohmann::json& j, const Movie& m) {
58 j = nlohmann::json{{"title", m.title}, {"year", m.year}, {"director", m.director}};
59 if (!m.plotShort.empty()) j["plotShort"] = m.plotShort;
60 if (!m.plotFull.empty()) j["plotFull"] = m.plotFull;
61 if (!m.actors.empty()) j["actors"] = m.actors;
62 if (!m.genres.empty()) j["genres"] = m.genres;
63 if (m.runtimeMinutes > 0) j["runtimeMinutes"] = m.runtimeMinutes;
64 if (!m.countries.empty()) j["countries"] = m.countries;
65 if (!m.posterUrl.empty()) j["posterUrl"] = m.posterUrl;
66 if (m.imdbRating > 0.0) j["imdbRating"] = m.imdbRating;
67 if (m.metascore > 0) j["metascore"] = m.metascore;
68 if (m.rottenTomatoes > 0) j["rottenTomatoes"] = m.rottenTomatoes;
69 if (!m.source.empty()) j["source"] = m.source;
70 if (!m.imdbID.empty()) j["imdbID"] = m.imdbID;
71 // Always persist ranking fields for stability across sessions
72 j["userScore"] = m.userScore;
73 j["userRank"] = m.userRank;
74}
75
82inline void from_json(const nlohmann::json& j, Movie& m) {
83 // Required legacy fields
84 if (j.contains("title")) j.at("title").get_to(m.title); else m.title = "";
85 if (j.contains("year")) j.at("year").get_to(m.year); else m.year = 0;
86 if (j.contains("director")) j.at("director").get_to(m.director); else m.director = "";
87
88 // Optional new fields (present in newer saves)
89 m.plotShort = j.value("plotShort", std::string());
90 m.plotFull = j.value("plotFull", std::string());
91 if (j.contains("actors")) j.at("actors").get_to(m.actors); else m.actors.clear();
92 if (j.contains("genres")) j.at("genres").get_to(m.genres); else m.genres.clear();
93 m.runtimeMinutes = j.value("runtimeMinutes", 0);
94 if (j.contains("countries")) j.at("countries").get_to(m.countries); else m.countries.clear();
95 m.posterUrl = j.value("posterUrl", "");
96 m.imdbRating = j.value("imdbRating", 0.0);
97 m.metascore = j.value("metascore", 0);
98 m.rottenTomatoes = j.value("rottenTomatoes", 0);
99 m.source = j.value("source", "");
100 m.imdbID = j.value("imdbID", "");
101 // User ranking fields (default for legacy files)
102 m.userScore = j.value("userScore", 1500.0);
103 m.userRank = j.value("userRank", -1);
104}
Movie domain model and metadata.
Definition Movie.h:27