diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-01-22 03:25:26 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-01-22 03:25:26 +0100 |
| commit | 10cdaaa30a6b4a187797434dc8d959780f0e8fbf (patch) | |
| tree | 6c050b5cfec8a06de09fc4eb8ce5d20ff761a169 /common | |
| parent | da27cc2f457f362f11f65b4e06e3d9eca09d1344 (diff) | |
| download | jellything-10cdaaa30a6b4a187797434dc8d959780f0e8fbf.tar jellything-10cdaaa30a6b4a187797434dc8d959780f0e8fbf.tar.bz2 jellything-10cdaaa30a6b4a187797434dc8d959780f0e8fbf.tar.zst | |
start on typedef for renderer types
Diffstat (limited to 'common')
| -rw-r--r-- | common/object/src/registry.rs | 2 | ||||
| -rw-r--r-- | common/src/api.rs | 185 | ||||
| -rw-r--r-- | common/src/lib.rs | 151 | ||||
| -rw-r--r-- | common/src/node.rs | 140 | ||||
| -rw-r--r-- | common/src/user.rs | 11 |
5 files changed, 241 insertions, 248 deletions
diff --git a/common/object/src/registry.rs b/common/object/src/registry.rs index c831b28..2cd2a1f 100644 --- a/common/object/src/registry.rs +++ b/common/object/src/registry.rs @@ -60,7 +60,7 @@ macro_rules! fields { macro_rules! enums { ($($id:ident = $tag:literal $name:literal;)*) => { $(pub const $id: $crate::Tag = $crate::Tag($tag);)* - fn register_enums(reg: &mut $crate::Registry) { + pub(crate) fn register_enums(reg: &mut $crate::Registry) { $(reg.add($crate::Tag($tag), $crate::TagInfo { name: $name, r#type: None });)* } }; diff --git a/common/src/api.rs b/common/src/api.rs index 0dc8f43..2123477 100644 --- a/common/src/api.rs +++ b/common/src/api.rs @@ -3,120 +3,97 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::user::{NodeUserData, User}; -use std::{collections::BTreeMap, sync::Arc, time::Duration}; -pub struct ApiNodeResponse { - pub parents: NodesWithUdata, - pub children: NodesWithUdata, - pub node: Arc<Node>, - pub userdata: NodeUserData, -} +use jellyobject::{Object, Tag, enums, fields}; -pub struct ApiSearchResponse { - pub count: usize, - pub results: NodesWithUdata, - pub duration: Duration, -} +fields! { + QUERY_PARENT: u64 = 2000 "parent"; + QUERY_SEARCH: &str = 2001 "search"; + QUERY_KIND: Tag = 2004 "kind"; // multi + QUERY_SORT: Tag = 2002 "sort"; // one of RTYP_*, NU_RATING, NO_DURATION, NO_NAME + QUERY_SORT_ASCENDING: () = 2003 "sort_ascending"; -pub struct ApiItemsResponse { - pub count: usize, - pub pages: usize, - pub items: NodesWithUdata, -} + VIEW_TITLE: &str = 2005 "title"; + VIEW_MESSAGE: &str = 2010 "message"; + VIEW_NODE_PAGE: Object = 2011 "node_page"; + VIEW_NODE_LIST: Object = 2012 "node_list"; // multi + VIEW_PLAYER: u64 = 2028 "player"; -pub struct ApiHomeResponse { - pub toplevel: NodesWithUdata, - pub categories: Vec<(String, NodesWithUdata)>, -} + NKU_NODE: Object = 2025 "node"; + NKU_UDATA: Object = 2026 "udata"; + NKU_ROLE: &str = 2027 "role"; -pub struct ApiStatsResponse { - pub kinds: BTreeMap<NodeKind, StatsBin>, - pub total: StatsBin, + NODELIST_TITLE: &str = 2007 "title"; + NODELIST_DISPLAYSTYLE: &str = 2008 "displaystyle"; + NODELIST_ITEM: &str = 2009 "item"; } -pub struct ApiAdminUsersResponse { - pub users: Vec<User>, +enums! { + NLSTYLE_GRID = 1023 "grid"; + NLSTYLE_INLINE = 1024 "inline"; + NLSTYLE_LIST = 1023 "list"; } -pub struct LogLine { - pub time: DateTime<Utc>, - pub module: Option<&'static str>, - pub level: LogLevel, - pub message: String, -} +// use crate::user::{NodeUserData, User}; +// use std::{collections::BTreeMap, sync::Arc, time::Duration}; +// pub struct ApiNodeResponse { +// pub parents: NodesWithUdata, +// pub children: NodesWithUdata, +// pub node: Arc<Node>, +// pub userdata: NodeUserData, +// } -url_enum!( - #[derive(Serialize, Deserialize, Clone, Copy, PartialEq)] - enum LogLevel { - Trace = "trace", - Debug = "debug", - Info = "info", - Warn = "warn", - Error = "error", - } -); +// pub struct ApiSearchResponse { +// pub count: usize, +// pub results: NodesWithUdata, +// pub duration: Duration, +// } -#[derive(Default, Serialize, Deserialize)] -pub struct StatsBin { - pub runtime: f64, - pub size: u64, - pub count: usize, - pub max_runtime: f64, - pub max_runtime_node: String, - pub max_size: u64, - pub max_size_node: String, -} +// pub struct ApiItemsResponse { +// pub count: usize, +// pub pages: usize, +// pub items: NodesWithUdata, +// } -#[derive(Debug, Default, Clone)] -pub struct NodeFilterSort { - pub sort_by: Option<SortProperty>, - pub filter_kind: Option<Vec<FilterProperty>>, - pub sort_order: Option<SortOrder>, -} +// pub struct ApiHomeResponse { +// pub toplevel: NodesWithUdata, +// pub categories: Vec<(String, NodesWithUdata)>, +// } + +// pub struct ApiStatsResponse { +// pub kinds: BTreeMap<NodeKind, StatsBin>, +// pub total: StatsBin, +// } + +// pub struct ApiAdminUsersResponse { +// pub users: Vec<User>, +// } + +// pub struct LogLine { +// pub time: DateTime<Utc>, +// pub module: Option<&'static str>, +// pub level: LogLevel, +// pub message: String, +// } + +// url_enum!( +// #[derive(Serialize, Deserialize, Clone, Copy, PartialEq)] +// enum LogLevel { +// Trace = "trace", +// Debug = "debug", +// Info = "info", +// Warn = "warn", +// Error = "error", +// } +// ); -url_enum!( - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - enum FilterProperty { - FederationLocal = "fed_local", - FederationRemote = "fed_remote", - Watched = "watched", - Unwatched = "unwatched", - WatchProgress = "watch_progress", - KindMovie = "kind_movie", - KindVideo = "kind_video", - KindShortFormVideo = "kind_short_form_video", - KindMusic = "kind_music", - KindCollection = "kind_collection", - KindChannel = "kind_channel", - KindShow = "kind_show", - KindSeries = "kind_series", - KindSeason = "kind_season", - KindEpisode = "kind_episode", - } -); -url_enum!( - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - enum SortProperty { - ReleaseDate = "release_date", - Title = "title", - Index = "index", - Duration = "duration", - RatingRottenTomatoes = "rating_rt", - RatingMetacritic = "rating_mc", - RatingImdb = "rating_imdb", - RatingTmdb = "rating_tmdb", - RatingYoutubeViews = "rating_yt_views", - RatingYoutubeLikes = "rating_yt_likes", - RatingYoutubeFollowers = "rating_yt_followers", - RatingUser = "rating_user", - RatingLikesDivViews = "rating_loved", - } -); -url_enum!( - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - enum SortOrder { - Ascending = "ascending", - Descending = "descending", - } -); +// #[derive(Default, Serialize, Deserialize)] +// pub struct StatsBin { +// pub runtime: f64, +// pub size: u64, +// pub count: usize, +// pub max_runtime: f64, +// pub max_runtime_node: String, +// pub max_size: u64, +// pub max_size_node: String, +// } diff --git a/common/src/lib.rs b/common/src/lib.rs index 38b0d05..af07e1c 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -3,19 +3,25 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin <metamuffin.org> */ -#![feature(array_try_map)] +pub mod api; +pub mod node; pub mod routes; pub mod user; -use jellyobject::{Object, Registry, Tag, TypedTag, enums, fields}; -pub use jellystream_types as stream; -use std::sync::LazyLock; pub use jellyobject; +pub use jellystream_types as stream; + +pub use api::*; +pub use node::*; +pub use user::*; + +use jellyobject::{Registry, TypedTag}; +use std::sync::LazyLock; pub static TAGREG: LazyLock<Registry> = LazyLock::new(|| { let mut reg = Registry::default(); - register_fields(&mut reg); - register_enums(&mut reg); + node::register_fields(&mut reg); + node::register_enums(&mut reg); user::register_fields(&mut reg); reg }); @@ -25,137 +31,4 @@ fn check_tag_conflicts() { let _ = &*TAGREG; } -fields! { - // Tag counter: 40 - - NO_KIND: Tag = 1 "kind"; - NO_TITLE: &str = 2 "title"; - NO_PARENT: u64 = 3 "parent"; // multi - NO_SUBTITLE: &str = 38 "subtitle"; - NO_TAGLINE: &str = 4 "tagline"; - NO_DESCRIPTION: &str = 5 "description"; - NO_RELEASEDATE: i64 = 6 "releasedate"; - NO_DURATION: f64 = 39 "duration"; - NO_INDEX: u64 = 7 "index"; - NO_SEASON_INDEX: u64 = 8 "season_index"; - NO_TRACK: Object = 9 "track"; // multi - NO_CHAPTER: Object = 32 "chapter"; // multi - NO_TAG: &str = 10 "tag"; // multi - NO_RATINGS: Object = 11 "ratings"; - NO_PICTURES: Object = 12 "pictures"; - NO_IDENTIFIERS: Object = 13 "identifiers"; - NO_VISIBILITY: Tag = 14 "visibility"; - NO_STORAGE_SIZE: u64 = 15 "storage_size"; - NO_CREDIT: Object = 33 "credit"; // multi - NO_SLUG: &str = 37 "slug"; - - CR_NODE: u64 = 34 "node"; - CR_KIND: Tag = 35 "kind"; - CR_ROLE: &str = 36 "role"; // multi - - TR_KIND: Tag = 16 "kind"; - TR_SOURCE: Object = 17 "source"; - TR_NAME: &str = 18 "name"; - TR_CODEC: &str = 19 "codec"; - TR_LANGUAGE: Tag = 20 "language"; - TR_RATE: f64 = 23 "rate"; - TR_BIT_DEPTH: u32 = 25 "bit_depth"; - TR_CHANNELS: u32 = 28 "channels"; - TR_PIXEL_WIDTH: u32 = 26 "pixel_width"; - TR_PIXEL_HEIGHT: u32 = 27 "pixel_height"; - - TRSOURCE_LOCAL_PATH: &str = 21 "local_path"; - TRSOURCE_LOCAL_TRACKNUM: u64 = 22 "local_tracknum"; - - CH_START: f64 = 29 "start"; - CH_END: f64 = 30 "end"; - CH_NAME: &str = 31 "name"; - - LANG_UND: &str = 0xa000 "und"; - LANG_NATIVE: &str = 0xa001 "native"; - LANG_ENG: &str = 0xa002 "eng"; - LANG_DEU: &str = 0xa003 "deu"; - LANG_JPN: &str = 0xa004 "jpn"; - - PICT_COVER: &str = 0xd001 "cover"; - PICT_BACKDROP: &str = 0xd002 "backdrop"; - - RTYP_IMDB: f64 = 0xf001 "imdb"; - RTYP_TMDB: f64 = 0xf002 "tmdb"; - RTYP_ROTTEN_TOMATOES: f64 = 0xf003 "rotten_tomatoes"; - RTYP_METACRITIC: f64 = 0xf004 "metacritic"; - RTYP_YOUTUBE_VIEWS: f64 = 0xf005 "youtube_views"; - RTYP_YOUTUBE_LIKES: f64 = 0xf006 "youtube_likes"; - RTYP_YOUTUBE_FOLLOWERS: f64 = 0xf007 "youtube_followers"; - RTYP_TRAKT: f64 = 0xf008 "trakt"; - - IDENT_MUSICBRAINZ_RECORDING: &str = 0xc001 "musicbrainz_recording"; - IDENT_MUSICBRAINZ_ARTIST: &str = 0xc002 "musicbrainz_artist"; - IDENT_MUSICBRAINZ_RELEASE: &str = 0xc003 "musicbrainz_release"; - IDENT_MUSICBRAINZ_RELEASE_GROUP: &str = 0xc004 "musicbrainz_release_group"; - IDENT_ACOUST_ID_TRACK: &str = 0xc005 "acoust_id_track"; - IDENT_YOUTUBE_VIDEO: &str = 0xc006 "youtube_video"; - IDENT_YOUTUBE_CHANNEL: &str = 0xc007 "youtube_channel"; - IDENT_YOUTUBE_CHANNEL_HANDLE: &str = 0xc008 "youtube_channel_handle"; - IDENT_BANDCAMP: &str = 0xc009 "bandcamp"; - IDENT_ISRC: &str = 0xc00a "isrc"; - IDENT_BARCODE: &str = 0xc00b "barcode"; - IDENT_TRAKT_MOVIE: &str = 0xc00c "trakt_movie"; - IDENT_TRAKT_SHOW: &str = 0xc00d "trakt_show"; - IDENT_TRAKT_SEASON: &str = 0xc00e "trakt_season"; - IDENT_TRAKT_EPISODE: &str = 0xc00f "trakt_episode"; - IDENT_IMDB: &str = 0xc010 "imdb"; - IDENT_TMDB_SERIES: &str = 0xc011 "tmdb_series"; - IDENT_TMDB_MOVIE: &str = 0xc012 "tmdb_movie"; - IDENT_TVDB: &str = 0xc013 "tvdb"; - IDENT_OMDB: &str = 0xc014 "omdb"; - IDENT_VGMDB_ARTIST: &str = 0xc015 "vgmdb_artist"; - -} - -pub type Kind = Tag; -pub type Language = Tag; - -enums! { - VISI_HIDDEN = 0xe001 "hidden"; - VISI_REDUCED = 0xe002 "reduced"; - VISI_VISIBLE = 0xe003 "visible"; - - TRKIND_VIDEO = 0x3001 "video"; - TRKIND_AUDIO = 0x3002 "audio"; - TRKIND_TEXT = 0x3003 "text"; - TRKIND_UNKNOWN = 0x3004 "unknown"; - - KIND_MOVIE = 0xb001 "movie"; - KIND_VIDEO = 0xb002 "video"; - KIND_MUSIC = 0xb003 "music"; - KIND_SHORTFORMVIDEO = 0xb004 "shortformvideo"; - KIND_COLLECTION = 0xb005 "collection"; - KIND_CHANNEL = 0xb006 "channel"; - KIND_SHOW = 0xb007 "show"; - KIND_SERIES = 0xb008 "series"; - KIND_SEASON = 0xb009 "season"; - KIND_EPISODE = 0xb00a "episode"; - - CRCAT_CAST = 0x2001 "cast"; - CRCAT_WRITING = 0x2002 "writing"; - CRCAT_DIRECTING = 0x2003 "directing"; - CRCAT_ART = 0x2004 "art"; - CRCAT_SOUND = 0x2005 "sound"; - CRCAT_CAMERA = 0x2006 "camera"; - CRCAT_LIGHTING = 0x2007 "lighting"; - CRCAT_CREW = 0x2008 "crew"; - CRCAT_EDITING = 0x2009 "editing"; - CRCAT_PRODUCTION = 0x200a "production"; - CRCAT_VFX = 0x200b "vfx"; - CRCAT_COSTUME_MAKEUP = 0x200c "costume_makeup"; - CRCAT_CREATED_BY = 0x200d "created_by"; - CRCAT_PERFORMANCE = 0x200e "performance"; - CRCAT_INSTRUMENT = 0x200f "instrument"; - CRCAT_VOCAL = 0x2010 "vocal"; - CRCAT_ARRANGER = 0x2011 "arranger"; - CRCAT_PRODUCER = 0x2012 "producer"; - CRCAT_ENGINEER = 0x2013 "engineer"; -} - pub struct Identifier(pub TypedTag<&'static str>, pub String); diff --git a/common/src/node.rs b/common/src/node.rs new file mode 100644 index 0000000..3163dc5 --- /dev/null +++ b/common/src/node.rs @@ -0,0 +1,140 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2025 metamuffin <metamuffin.org> +*/ + +use jellyobject::{Object, Tag, enums, fields}; + +fields! { + // Tag counter: 111 + + NO_KIND: Tag = 1 "kind"; + NO_TITLE: &str = 2 "title"; + NO_PARENT: u64 = 3 "parent"; // multi + NO_SUBTITLE: &str = 38 "subtitle"; + NO_TAGLINE: &str = 4 "tagline"; + NO_DESCRIPTION: &str = 5 "description"; + NO_RELEASEDATE: i64 = 6 "releasedate"; + NO_DURATION: f64 = 39 "duration"; + NO_INDEX: u64 = 7 "index"; + NO_SEASON_INDEX: u64 = 8 "season_index"; + NO_TRACK: Object = 9 "track"; // multi + NO_CHAPTER: Object = 32 "chapter"; // multi + NO_TAG: &str = 10 "tag"; // multi + NO_RATINGS: Object = 11 "ratings"; + NO_PICTURES: Object = 12 "pictures"; + NO_IDENTIFIERS: Object = 13 "identifiers"; + NO_VISIBILITY: Tag = 14 "visibility"; + NO_STORAGE_SIZE: u64 = 15 "storage_size"; + NO_CREDIT: Object = 33 "credit"; // multi + NO_SLUG: &str = 37 "slug"; + + CR_NODE: u64 = 34 "node"; + CR_KIND: Tag = 35 "kind"; + CR_ROLE: &str = 36 "role"; // multi + + TR_KIND: Tag = 16 "kind"; + TR_SOURCE: Object = 17 "source"; + TR_NAME: &str = 18 "name"; + TR_CODEC: &str = 19 "codec"; + TR_LANGUAGE: Tag = 20 "language"; + TR_RATE: f64 = 23 "rate"; + TR_BIT_DEPTH: u32 = 25 "bit_depth"; + TR_CHANNELS: u32 = 28 "channels"; + TR_PIXEL_WIDTH: u32 = 26 "pixel_width"; + TR_PIXEL_HEIGHT: u32 = 27 "pixel_height"; + + TRSOURCE_LOCAL_PATH: &str = 21 "local_path"; + TRSOURCE_LOCAL_TRACKNUM: u64 = 22 "local_tracknum"; + + CH_START: f64 = 29 "start"; + CH_END: f64 = 30 "end"; + CH_NAME: &str = 31 "name"; + + LANG_UND: &str = 40 "und"; + LANG_NATIVE: &str = 41 "native"; + LANG_ENG: &str = 42 "eng"; + LANG_DEU: &str = 43 "deu"; + LANG_JPN: &str = 44 "jpn"; + + PICT_COVER: &str = 45 "cover"; + PICT_BACKDROP: &str = 46 "backdrop"; + + RTYP_IMDB: f64 = 47 "imdb"; + RTYP_TMDB: f64 = 48 "tmdb"; + RTYP_ROTTEN_TOMATOES: f64 = 49 "rotten_tomatoes"; + RTYP_METACRITIC: f64 = 50 "metacritic"; + RTYP_YOUTUBE_VIEWS: f64 = 51 "youtube_views"; + RTYP_YOUTUBE_LIKES: f64 = 52 "youtube_likes"; + RTYP_YOUTUBE_FOLLOWERS: f64 = 53 "youtube_followers"; + RTYP_TRAKT: f64 = 54 "trakt"; + + IDENT_MUSICBRAINZ_RECORDING: &str = 55 "musicbrainz_recording"; + IDENT_MUSICBRAINZ_ARTIST: &str = 56 "musicbrainz_artist"; + IDENT_MUSICBRAINZ_RELEASE: &str = 57 "musicbrainz_release"; + IDENT_MUSICBRAINZ_RELEASE_GROUP: &str = 58 "musicbrainz_release_group"; + IDENT_ACOUST_ID_TRACK: &str = 59 "acoust_id_track"; + IDENT_YOUTUBE_VIDEO: &str = 60 "youtube_video"; + IDENT_YOUTUBE_CHANNEL: &str = 61 "youtube_channel"; + IDENT_YOUTUBE_CHANNEL_HANDLE: &str = 62 "youtube_channel_handle"; + IDENT_BANDCAMP: &str = 63 "bandcamp"; + IDENT_ISRC: &str = 64 "isrc"; + IDENT_BARCODE: &str = 65 "barcode"; + IDENT_TRAKT_MOVIE: &str = 66 "trakt_movie"; + IDENT_TRAKT_SHOW: &str = 67 "trakt_show"; + IDENT_TRAKT_SEASON: &str = 68 "trakt_season"; + IDENT_TRAKT_EPISODE: &str = 69 "trakt_episode"; + IDENT_IMDB: &str = 70 "imdb"; + IDENT_TMDB_SERIES: &str = 71 "tmdb_series"; + IDENT_TMDB_MOVIE: &str = 72 "tmdb_movie"; + IDENT_TVDB: &str = 73 "tvdb"; + IDENT_OMDB: &str = 74 "omdb"; + IDENT_VGMDB_ARTIST: &str = 75 "vgmdb_artist"; + +} + +pub type Kind = Tag; +pub type Language = Tag; + +enums! { + VISI_HIDDEN = 76 "hidden"; + VISI_REDUCED = 77 "reduced"; + VISI_VISIBLE = 78 "visible"; + + TRKIND_VIDEO = 79 "video"; + TRKIND_AUDIO = 80 "audio"; + TRKIND_TEXT = 81 "text"; + TRKIND_UNKNOWN = 82 "unknown"; + + KIND_MOVIE = 83 "movie"; + KIND_VIDEO = 84 "video"; + KIND_MUSIC = 85 "music"; + KIND_SHORTFORMVIDEO = 86 "shortformvideo"; + KIND_COLLECTION = 87 "collection"; + KIND_CHANNEL = 88 "channel"; + KIND_SHOW = 89 "show"; + KIND_SERIES = 90 "series"; + KIND_SEASON = 91 "season"; + KIND_EPISODE = 92 "episode"; + + CRCAT_CAST = 93 "cast"; + CRCAT_WRITING = 94 "writing"; + CRCAT_DIRECTING = 95 "directing"; + CRCAT_ART = 96 "art"; + CRCAT_SOUND = 97 "sound"; + CRCAT_CAMERA = 98 "camera"; + CRCAT_LIGHTING = 99 "lighting"; + CRCAT_CREW = 100 "crew"; + CRCAT_EDITING = 101 "editing"; + CRCAT_PRODUCTION = 102 "production"; + CRCAT_VFX = 0x200b "vfx"; + CRCAT_COSTUME_MAKEUP = 103 "costume_makeup"; + CRCAT_CREATED_BY = 104 "created_by"; + CRCAT_PERFORMANCE = 105 "performance"; + CRCAT_INSTRUMENT = 106 "instrument"; + CRCAT_VOCAL = 107 "vocal"; + CRCAT_ARRANGER = 108 "arranger"; + CRCAT_PRODUCER = 109 "producer"; + CRCAT_ENGINEER = 110 "engineer"; +} diff --git a/common/src/user.rs b/common/src/user.rs index f4aaa0b..939438d 100644 --- a/common/src/user.rs +++ b/common/src/user.rs @@ -7,8 +7,11 @@ use jellyobject::fields; fields! { - USER_LOGIN: &str = 0x1001 "login"; - USER_PASSWORD: &str = 0x1002 "password"; - USER_NAME: &str = 0x1003 "name"; - USER_ADMIN: () = 0x1004 "admin"; + USER_LOGIN: &str = 1001 "login"; + USER_PASSWORD: &str = 1002 "password"; + USER_NAME: &str = 1003 "name"; + USER_ADMIN: () = 1004 "admin"; + + UDATA_WATCHED: () = 1005 "watched"; + UDATA_RATING: f64 = 1006 "rating"; } |