Skip to content
This repository

Tagging support for tvshows and musicvideos #1400

Merged
merged 3 commits into from over 1 year ago

2 participants

Sascha Montellese jmarshallnz
Sascha Montellese
Collaborator

A while ago I added tags for movies as a replacement for sets as we added a limitation that every movie can only be part of one set. This PR extends the tagging support to tvshows and musicvideos and includes some refactoring of the code in CGUIWindowVideoNav to make it more general (i.e. work for movies, tvshows and musicvideos).

jmarshallnz

There's an unrelated gnu address change there but otherwise all good.

Sascha Montellese
Collaborator

@jmarshallnz Thanks for the hint. I removed it as it's already in master anyway.

Sascha Montellese Montellese merged commit 9f0b31c into from September 10, 2012
Sascha Montellese Montellese closed this September 10, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
5  system/library/video/musicvideos/tags.xml
... ...
@@ -0,0 +1,5 @@
  1
+<node order="8" type="folder">
  2
+  <label>20459</label>
  3
+  <path>videodb://3/9</path>
  4
+  <icon>DefaultTags.png</icon>
  5
+</node>
5  system/library/video/tvshows/tags.xml
... ...
@@ -0,0 +1,5 @@
  1
+<node order="6" type="folder">
  2
+  <label>20459</label>
  3
+  <path>videodb://2/9</path>
  4
+  <icon>DefaultTags.png</icon>
  5
+</node>
1  xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMusicVideosOverview.cpp
@@ -34,6 +34,7 @@ Node MusicVideoChildren[] = {
34 34
                               { NODE_TYPE_MUSICVIDEOS_ALBUM, 5, 132 },
35 35
                               { NODE_TYPE_DIRECTOR,          6, 20348 },
36 36
                               { NODE_TYPE_STUDIO,            7, 20388 },
  37
+                              { NODE_TYPE_TAGS,              9, 20459 }
37 38
                             };
38 39
 
39 40
 CDirectoryNodeMusicVideosOverview::CDirectoryNodeMusicVideosOverview(const CStdString& strName, CDirectoryNode* pParent)
9  xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTags.cpp
@@ -33,7 +33,14 @@ CDirectoryNodeTags::CDirectoryNodeTags(const CStdString& strName, CDirectoryNode
33 33
 
34 34
 NODE_TYPE CDirectoryNodeTags::GetChildType() const
35 35
 {
36  
-  return NODE_TYPE_TITLE_MOVIES;
  36
+  CQueryParams params;
  37
+  CollectQueryParams(params);
  38
+  if (params.GetContentType() == VIDEODB_CONTENT_MOVIES)
  39
+    return NODE_TYPE_TITLE_MOVIES;
  40
+  if (params.GetContentType() == VIDEODB_CONTENT_MUSICVIDEOS)
  41
+    return NODE_TYPE_TITLE_MUSICVIDEOS;
  42
+
  43
+  return NODE_TYPE_TITLE_TVSHOWS;
37 44
 }
38 45
 
39 46
 CStdString CDirectoryNodeTags::GetLocalizedName() const
2  xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTitleMusicVideos.cpp
@@ -40,7 +40,7 @@ bool CDirectoryNodeTitleMusicVideos::GetContent(CFileItemList& items) const
40 40
   CQueryParams params;
41 41
   CollectQueryParams(params);
42 42
 
43  
-  bool bSuccess=videodatabase.GetMusicVideosNav(BuildPath(), items, params.GetGenreId(), params.GetYear(), params.GetActorId(), params.GetDirectorId(),params.GetStudioId(),params.GetAlbumId());
  43
+  bool bSuccess=videodatabase.GetMusicVideosNav(BuildPath(), items, params.GetGenreId(), params.GetYear(), params.GetActorId(), params.GetDirectorId(), params.GetStudioId(), params.GetAlbumId(), params.GetTagId());
44 44
 
45 45
   videodatabase.Close();
46 46
 
2  xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTitleTvShows.cpp
@@ -53,7 +53,7 @@ bool CDirectoryNodeTitleTvShows::GetContent(CFileItemList& items) const
53 53
   CQueryParams params;
54 54
   CollectQueryParams(params);
55 55
 
56  
-  bool bSuccess=videodatabase.GetTvShowsNav(BuildPath(), items, params.GetGenreId(), params.GetYear(), params.GetActorId(), params.GetDirectorId(), params.GetStudioId());
  56
+  bool bSuccess=videodatabase.GetTvShowsNav(BuildPath(), items, params.GetGenreId(), params.GetYear(), params.GetActorId(), params.GetDirectorId(), params.GetStudioId(), params.GetTagId());
57 57
 
58 58
   videodatabase.Close();
59 59
 
1  xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTvShowsOverview.cpp
@@ -32,6 +32,7 @@ Node TvShowChildren[] = {
32 32
                           { NODE_TYPE_YEAR,          3, 562 },
33 33
                           { NODE_TYPE_ACTOR,         4, 344 },
34 34
                           { NODE_TYPE_STUDIO,        5, 20388 },
  35
+                          { NODE_TYPE_TAGS,          9, 20459 }
35 36
                         };
36 37
 
37 38
 CDirectoryNodeTvShowsOverview::CDirectoryNodeTvShowsOverview(const CStdString& strName, CDirectoryNode* pParent)
20  xbmc/interfaces/json-rpc/ServiceDescription.h
@@ -455,7 +455,7 @@ namespace JSONRPC
455 455
                   "\"imdbnumber\", \"premiered\", \"votes\", \"lastplayed\","
456 456
                   "\"fanart\", \"thumbnail\", \"file\", \"originaltitle\","
457 457
                   "\"sorttitle\", \"episodeguide\", \"season\", \"watchedepisodes\","
458  
-                  "\"dateadded\"]"
  458
+                  "\"dateadded\", \"tag\" ]"
459 459
       "}"
460 460
     "}",
461 461
     "\"Video.Fields.Season\": {"
@@ -483,7 +483,7 @@ namespace JSONRPC
483 483
         "\"enum\": [ \"title\", \"playcount\", \"runtime\", \"director\","
484 484
                   "\"studio\", \"year\", \"plot\", \"album\", \"artist\","
485 485
                   "\"genre\", \"track\", \"streamdetails\", \"lastplayed\","
486  
-                  "\"fanart\", \"thumbnail\", \"file\", \"resume\", \"dateadded\" ]"
  486
+                  "\"fanart\", \"thumbnail\", \"file\", \"resume\", \"dateadded\", \"tag\" ]"
487 487
       "}"
488 488
     "}",
489 489
     "\"Video.Cast\": {"
@@ -629,7 +629,8 @@ namespace JSONRPC
629 629
         "\"premiered\": { \"type\": \"string\" },"
630 630
         "\"votes\": { \"type\": \"string\" },"
631 631
         "\"episodeguide\": { \"type\": \"string\" },"
632  
-        "\"season\": { \"type\": \"integer\" }"
  632
+        "\"season\": { \"type\": \"integer\" },"
  633
+        "\"tag\": { \"$ref\": \"Array.String\" }"
633 634
       "}"
634 635
     "}",
635 636
     "\"Video.Details.Season\": {"
@@ -668,7 +669,8 @@ namespace JSONRPC
668 669
         "\"album\": { \"type\": \"string\" },"
669 670
         "\"artist\": { \"$ref\": \"Array.String\" },"
670 671
         "\"genre\": { \"$ref\": \"Array.String\" },"
671  
-        "\"track\": { \"type\": \"integer\" }"
  672
+        "\"track\": { \"type\": \"integer\" },"
  673
+        "\"tag\": { \"$ref\": \"Array.String\" }"
672 674
       "}"
673 675
     "}",
674 676
     "\"Files.Media\": {"
@@ -1382,7 +1384,7 @@ namespace JSONRPC
1382 1384
       "\"transport\": \"Response\","
1383 1385
       "\"permission\": \"ControlPlayback\","
1384 1386
       "\"params\": ["
1385  
-        "{ \"name\": \"playerid\", \"$ref\": \"Player.Id\", \"required\": true },"
  1387
+        "{ \"name\": \"playerid\", \"$ref\": \"Player.Id\", \"required\": true }"
1386 1388
         "{ \"name\": \"value\", \"type\": \"string\", \"enum\": [ \"clockwise\", \"counterclockwise\" ], \"default\": \"clockwise\" }"
1387 1389
       "],"
1388 1390
       "\"returns\": \"string\""
@@ -2116,6 +2118,7 @@ namespace JSONRPC
2116 2118
             "{ \"type\": \"object\", \"properties\": { \"year\": { \"type\": \"integer\", \"minimum\": 0, \"required\": true } }, \"additionalProperties\": false },"
2117 2119
             "{ \"type\": \"object\", \"properties\": { \"actor\": { \"type\": \"string\", \"minLength\": 1, \"required\": true } }, \"additionalProperties\": false },"
2118 2120
             "{ \"type\": \"object\", \"properties\": { \"studio\": { \"type\": \"string\", \"minLength\": 1, \"required\": true } }, \"additionalProperties\": false },"
  2121
+            "{ \"type\": \"object\", \"properties\": { \"tag\": { \"type\": \"string\", \"minLength\": 1, \"required\": true } }, \"additionalProperties\": false },"
2119 2122
             "{ \"$ref\": \"List.Filter.TVShows\" }"
2120 2123
           "]"
2121 2124
         "}"
@@ -2227,6 +2230,7 @@ namespace JSONRPC
2227 2230
             "{ \"type\": \"object\", \"properties\": { \"year\": { \"type\": \"integer\", \"minimum\": 0, \"required\": true } }, \"additionalProperties\": false },"
2228 2231
             "{ \"type\": \"object\", \"properties\": { \"director\": { \"type\": \"string\", \"minLength\": 1, \"required\": true } }, \"additionalProperties\": false },"
2229 2232
             "{ \"type\": \"object\", \"properties\": { \"studio\": { \"type\": \"string\", \"minLength\": 1, \"required\": true } }, \"additionalProperties\": false },"
  2233
+            "{ \"type\": \"object\", \"properties\": { \"tag\": { \"type\": \"string\", \"minLength\": 1, \"required\": true } }, \"additionalProperties\": false },"
2230 2234
             "{ \"$ref\": \"List.Filter.MusicVideos\" }"
2231 2235
           "]"
2232 2236
         "}"
@@ -2391,7 +2395,8 @@ namespace JSONRPC
2391 2395
         "{ \"name\": \"sorttitle\", \"$ref\": \"Optional.String\" },"
2392 2396
         "{ \"name\": \"episodeguide\", \"$ref\": \"Optional.String\" },"
2393 2397
         "{ \"name\": \"thumbnail\", \"$ref\": \"Optional.String\" },"
2394  
-        "{ \"name\": \"fanart\", \"$ref\": \"Optional.String\" }"
  2398
+        "{ \"name\": \"fanart\", \"$ref\": \"Optional.String\" },"
  2399
+        "{ \"name\": \"tag\", \"type\": [ \"null\", { \"$ref\": \"Array.String\", \"required\": true } ], \"default\": null }"
2395 2400
       "],"
2396 2401
       "\"returns\": \"string\""
2397 2402
     "}",
@@ -2441,7 +2446,8 @@ namespace JSONRPC
2441 2446
         "{ \"name\": \"track\", \"$ref\": \"Optional.Integer\" },"
2442 2447
         "{ \"name\": \"lastplayed\", \"$ref\": \"Optional.String\" },"
2443 2448
         "{ \"name\": \"thumbnail\", \"$ref\": \"Optional.String\" },"
2444  
-        "{ \"name\": \"fanart\", \"$ref\": \"Optional.String\" }"
  2449
+        "{ \"name\": \"fanart\", \"$ref\": \"Optional.String\" },"
  2450
+        "{ \"name\": \"tag\", \"type\": [ \"null\", { \"$ref\": \"Array.String\", \"required\": true } ], \"default\": null }"
2445 2451
       "],"
2446 2452
       "\"returns\": \"string\""
2447 2453
     "}",
10  xbmc/interfaces/json-rpc/VideoLibrary.cpp
@@ -170,6 +170,8 @@ JSONRPC_STATUS CVideoLibrary::GetTVShows(const CStdString &method, ITransportLay
170 170
     videoUrl.AddOption("actor", filter["actor"].asString());
171 171
   else if (filter.isMember("studio"))
172 172
     videoUrl.AddOption("studio", filter["studio"].asString());
  173
+  else if (filter.isMember("tag"))
  174
+    videoUrl.AddOption("tag", filter["tag"].asString());
173 175
   else if (filter.isObject())
174 176
   {
175 177
     CStdString xsp;
@@ -180,14 +182,14 @@ JSONRPC_STATUS CVideoLibrary::GetTVShows(const CStdString &method, ITransportLay
180 182
   }
181 183
 
182 184
   CFileItemList items;
183  
-  if (!videodatabase.GetTvShowsNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, sorting))
  185
+  if (!videodatabase.GetTvShowsNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, sorting))
184 186
     return InvalidParams;
185 187
 
186 188
   bool additionalInfo = false;
187 189
   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
188 190
   {
189 191
     CStdString fieldValue = itr->asString();
190  
-    if (fieldValue == "cast")
  192
+    if (fieldValue == "cast" || fieldValue == "tag")
191 193
       additionalInfo = true;
192 194
   }
193 195
 
@@ -351,6 +353,8 @@ JSONRPC_STATUS CVideoLibrary::GetMusicVideos(const CStdString &method, ITranspor
351 353
     videoUrl.AddOption("director", filter["director"].asString());
352 354
   else if (filter.isMember("studio"))
353 355
     videoUrl.AddOption("studio", filter["studio"].asString());
  356
+  else if (filter.isMember("tag"))
  357
+    videoUrl.AddOption("tag", filter["tag"].asString());
354 358
   else if (filter.isObject())
355 359
   {
356 360
     CStdString xsp;
@@ -361,7 +365,7 @@ JSONRPC_STATUS CVideoLibrary::GetMusicVideos(const CStdString &method, ITranspor
361 365
   }
362 366
 
363 367
   CFileItemList items;
364  
-  if (!videodatabase.GetMusicVideosNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, sorting))
  368
+  if (!videodatabase.GetMusicVideosNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, -1, sorting))
365 369
     return InternalError;
366 370
 
367 371
   return GetAdditionalMusicVideoDetails(parameterObject, items, result, videodatabase, false);
8  xbmc/interfaces/json-rpc/methods.json
@@ -1051,6 +1051,7 @@
1051 1051
           { "type": "object", "properties": { "year": { "type": "integer", "minimum": 0, "required": true } }, "additionalProperties": false },
1052 1052
           { "type": "object", "properties": { "actor": { "type": "string", "minLength": 1, "required": true } }, "additionalProperties": false },
1053 1053
           { "type": "object", "properties": { "studio": { "type": "string", "minLength": 1, "required": true } }, "additionalProperties": false },
  1054
+          { "type": "object", "properties": { "tag": { "type": "string", "minLength": 1, "required": true } }, "additionalProperties": false },
1054 1055
           { "$ref": "List.Filter.TVShows" }
1055 1056
         ]
1056 1057
       }
@@ -1162,6 +1163,7 @@
1162 1163
           { "type": "object", "properties": { "year": { "type": "integer", "minimum": 0, "required": true } }, "additionalProperties": false },
1163 1164
           { "type": "object", "properties": { "director": { "type": "string", "minLength": 1, "required": true } }, "additionalProperties": false },
1164 1165
           { "type": "object", "properties": { "studio": { "type": "string", "minLength": 1, "required": true } }, "additionalProperties": false },
  1166
+          { "type": "object", "properties": { "tag": { "type": "string", "minLength": 1, "required": true } }, "additionalProperties": false },
1165 1167
           { "$ref": "List.Filter.MusicVideos" }
1166 1168
         ]
1167 1169
       }
@@ -1326,7 +1328,8 @@
1326 1328
       { "name": "sorttitle", "$ref": "Optional.String" },
1327 1329
       { "name": "episodeguide", "$ref": "Optional.String" },
1328 1330
       { "name": "thumbnail", "$ref": "Optional.String" },
1329  
-      { "name": "fanart", "$ref": "Optional.String" }
  1331
+      { "name": "fanart", "$ref": "Optional.String" },
  1332
+      { "name": "tag", "type": [ "null", { "$ref": "Array.String", "required": true } ], "default": null }
1330 1333
     ],
1331 1334
     "returns": "string"
1332 1335
   },
@@ -1376,7 +1379,8 @@
1376 1379
       { "name": "track", "$ref": "Optional.Integer" },
1377 1380
       { "name": "lastplayed", "$ref": "Optional.String" },
1378 1381
       { "name": "thumbnail", "$ref": "Optional.String" },
1379  
-      { "name": "fanart", "$ref": "Optional.String" }
  1382
+      { "name": "fanart", "$ref": "Optional.String" },
  1383
+      { "name": "tag", "type": [ "null", { "$ref": "Array.String", "required": true } ], "default": null }
1380 1384
     ],
1381 1385
     "returns": "string"
1382 1386
   },
10  xbmc/interfaces/json-rpc/types.json
@@ -427,7 +427,7 @@
427 427
                 "imdbnumber", "premiered", "votes", "lastplayed",
428 428
                 "fanart", "thumbnail", "file", "originaltitle",
429 429
                 "sorttitle", "episodeguide", "season", "watchedepisodes",
430  
-                "dateadded"]
  430
+                "dateadded", "tag" ]
431 431
     }
432 432
   },
433 433
   "Video.Fields.Season": {
@@ -455,7 +455,7 @@
455 455
       "enum": [ "title", "playcount", "runtime", "director",
456 456
                 "studio", "year", "plot", "album", "artist",
457 457
                 "genre", "track", "streamdetails", "lastplayed",
458  
-                "fanart", "thumbnail", "file", "resume", "dateadded" ]
  458
+                "fanart", "thumbnail", "file", "resume", "dateadded", "tag" ]
459 459
     }
460 460
   },
461 461
   "Video.Cast": {
@@ -601,7 +601,8 @@
601 601
       "premiered": { "type": "string" },
602 602
       "votes": { "type": "string" },
603 603
       "episodeguide": { "type": "string" },
604  
-      "season": { "type": "integer" }
  604
+      "season": { "type": "integer" },
  605
+      "tag": { "$ref": "Array.String" }
605 606
     }
606 607
   },
607 608
   "Video.Details.Season": {
@@ -640,7 +641,8 @@
640 641
       "album": { "type": "string" },
641 642
       "artist": { "$ref": "Array.String" },
642 643
       "genre": { "$ref": "Array.String" },
643  
-      "track": { "type": "integer" }
  644
+      "track": { "type": "integer" },
  645
+      "tag": { "$ref": "Array.String" }
644 646
     }
645 647
   },
646 648
   "Files.Media": {
90  xbmc/video/VideoDatabase.cpp
@@ -2077,6 +2077,13 @@ int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoI
2077 2077
     for (i = 0; i < vecStudios.size(); ++i)
2078 2078
       AddStudioToTvShow(idTvShow, vecStudios[i]);
2079 2079
 
  2080
+    // add tags...
  2081
+    for (unsigned int i = 0; i < details.m_tags.size(); i++)
  2082
+    {
  2083
+      int idTag = AddTag(details.m_tags[i]);
  2084
+      AddTagToItem(idTvShow, idTag, "tvshow");
  2085
+    }
  2086
+
2080 2087
     // add "all seasons" - the rest are added in SetDetailsForEpisode
2081 2088
     AddSeason(idTvShow, -1);
2082 2089
 
@@ -2255,6 +2262,13 @@ int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath
2255 2262
       AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2256 2263
     }
2257 2264
 
  2265
+    // add tags...
  2266
+    for (unsigned int i = 0; i < details.m_tags.size(); i++)
  2267
+    {
  2268
+      int idTag = AddTag(details.m_tags[i]);
  2269
+      AddTagToItem(idMVideo, idTag, "musicvideo");
  2270
+    }
  2271
+
2258 2272
     if (details.HasStreamDetails())
2259 2273
       SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2260 2274
 
@@ -2939,15 +2953,25 @@ void CVideoDatabase::DeleteSet(int idSet)
2939 2953
   }
2940 2954
 }
2941 2955
 
2942  
-void CVideoDatabase::DeleteTag(int idTag, const std::string &mediaType)
  2956
+void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
2943 2957
 {
2944 2958
   try
2945 2959
   {
2946 2960
     if (m_pDB.get() == NULL || m_pDS.get() == NULL)
2947 2961
       return;
2948 2962
 
  2963
+    std::string type;
  2964
+    if (mediaType == VIDEODB_CONTENT_MOVIES)
  2965
+      type = "movie";
  2966
+    else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
  2967
+      type = "tvshow";
  2968
+    else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
  2969
+      type = "musicvideo";
  2970
+    else
  2971
+      return;
  2972
+
2949 2973
     CStdString strSQL;
2950  
-    strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, mediaType.c_str());
  2974
+    strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
2951 2975
     m_pDS->exec(strSQL.c_str());
2952 2976
 
2953 2977
     // check if the tag is used for another media type as well before deleting it completely
@@ -3242,6 +3266,15 @@ CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* con
3242 3266
   {
3243 3267
     GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3244 3268
 
  3269
+    // get tags
  3270
+    CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'tvshow' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idTvShow);
  3271
+    m_pDS2->query(strSQL.c_str());
  3272
+    while (!m_pDS2->eof())
  3273
+    {
  3274
+      details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
  3275
+      m_pDS2->next();
  3276
+    }
  3277
+
3245 3278
     castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3246 3279
     details.m_strPictureURL.Parse();
3247 3280
   }
@@ -3313,10 +3346,10 @@ CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record*
3313 3346
   CVideoInfoTag details;
3314 3347
 
3315 3348
   unsigned int time = XbmcThreads::SystemClockMillis();
3316  
-  int idMovie = record->at(0).get_asInt();
  3349
+  int idMVideo = record->at(0).get_asInt();
3317 3350
 
3318 3351
   GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3319  
-  details.m_iDbId = idMovie;
  3352
+  details.m_iDbId = idMVideo;
3320 3353
   details.m_type = "musicvideo";
3321 3354
   
3322 3355
   details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
@@ -3332,6 +3365,15 @@ CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record*
3332 3365
 
3333 3366
   movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3334 3367
 
  3368
+  // get tags
  3369
+  CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'musicvideo' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idMVideo);
  3370
+  m_pDS2->query(strSQL.c_str());
  3371
+  while (!m_pDS2->eof())
  3372
+  {
  3373
+    details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
  3374
+    m_pDS2->next();
  3375
+  }
  3376
+
3335 3377
   details.m_strPictureURL.Parse();
3336 3378
   return details;
3337 3379
 }
@@ -4550,6 +4592,10 @@ bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& ite
4550 4592
   CStdString mediaType;
4551 4593
   if (idContent == VIDEODB_CONTENT_MOVIES)
4552 4594
     mediaType = "movie";
  4595
+  else if (idContent == VIDEODB_CONTENT_TVSHOWS)
  4596
+    mediaType = "tvshow";
  4597
+  else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
  4598
+    mediaType = "musicvideo";
4553 4599
   else
4554 4600
     return false;
4555 4601
 
@@ -5657,7 +5703,7 @@ bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter
5657 5703
 }
5658 5704
 
5659 5705
 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
5660  
-                                  int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */,
  5706
+                                  int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
5661 5707
                                   const SortDescription &sortDescription /* = SortDescription() */)
5662 5708
 {
5663 5709
   CVideoDbUrl videoUrl;
@@ -5674,6 +5720,8 @@ bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList&
5674 5720
     videoUrl.AddOption("year", idYear);
5675 5721
   else if (idActor != -1)
5676 5722
     videoUrl.AddOption("actorid", idActor);
  5723
+  else if (idTag != -1)
  5724
+    videoUrl.AddOption("tagid", idTag);
5677 5725
 
5678 5726
   Filter filter;
5679 5727
   return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
@@ -6079,7 +6127,7 @@ bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filt
6079 6127
   return false;
6080 6128
 }
6081 6129
 
6082  
-bool CVideoDatabase::GetMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idArtist, int idDirector, int idStudio, int idAlbum, const SortDescription &sortDescription /* = SortDescription() */)
  6130
+bool CVideoDatabase::GetMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idArtist, int idDirector, int idStudio, int idAlbum, int idTag /* = -1 */, const SortDescription &sortDescription /* = SortDescription() */)
6083 6131
 {
6084 6132
   CVideoDbUrl videoUrl;
6085 6133
   if (!videoUrl.FromString(strBaseDir))
@@ -6095,6 +6143,8 @@ bool CVideoDatabase::GetMusicVideosNav(const CStdString& strBaseDir, CFileItemLi
6095 6143
     videoUrl.AddOption("year", idYear);
6096 6144
   else if (idArtist != -1)
6097 6145
     videoUrl.AddOption("artistid", idArtist);
  6146
+  else if (idTag != -1)
  6147
+    videoUrl.AddOption("tagid", idTag);
6098 6148
   if (idAlbum != -1)
6099 6149
     videoUrl.AddOption("albumid", idAlbum);
6100 6150
 
@@ -8994,6 +9044,20 @@ bool CVideoDatabase::GetFilter(const CDbUrl &videoUrl, Filter &filter)
8994 9044
         filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
8995 9045
         filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
8996 9046
       }
  9047
+
  9048
+      option = options.find("tagid");
  9049
+      if (option != options.end())
  9050
+      {
  9051
+        filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
  9052
+        filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
  9053
+      }
  9054
+
  9055
+      option = options.find("tag");
  9056
+      if (option != options.end())
  9057
+      {
  9058
+        filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
  9059
+        filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
  9060
+      }
8997 9061
     }
8998 9062
     else if (itemType == "seasons")
8999 9063
     {
@@ -9195,6 +9259,20 @@ bool CVideoDatabase::GetFilter(const CDbUrl &videoUrl, Filter &filter)
9195 9259
     option = options.find("albumid");
9196 9260
     if (option != options.end())
9197 9261
       filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = (select c%02d from musicvideo where idMVideo = %i)", VIDEODB_ID_MUSICVIDEO_ALBUM, VIDEODB_ID_MUSICVIDEO_ALBUM, (int)option->second.asInteger()));
  9262
+
  9263
+    option = options.find("tagid");
  9264
+    if (option != options.end())
  9265
+    {
  9266
+      filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
  9267
+      filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
  9268
+    }
  9269
+
  9270
+    option = options.find("tag");
  9271
+    if (option != options.end())
  9272
+    {
  9273
+      filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
  9274
+      filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
  9275
+    }
9198 9276
   }
9199 9277
   else
9200 9278
     return false;
6  xbmc/video/VideoDatabase.h
@@ -459,7 +459,7 @@ class CVideoDatabase : public CDatabase
459 459
   void RemoveContentForPath(const CStdString& strPath,CGUIDialogProgress *progress = NULL);
460 460
   void UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type);
461 461
   void DeleteSet(int idSet);
462  
-  void DeleteTag(int idTag, const std::string &mediaType);
  462
+  void DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType);
463 463
 
464 464
   // per-file video settings
465 465
   bool GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings);
@@ -589,10 +589,10 @@ class CVideoDatabase : public CDatabase
589 589
   bool GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist, const Filter &filter = Filter());
590 590
 
591 591
   bool GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idActor=-1, int idDirector=-1, int idStudio=-1, int idCountry=-1, int idSet=-1, int idTag=-1, const SortDescription &sortDescription = SortDescription());
592  
-  bool GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idActor=-1, int idDirector=-1, int idStudio=-1, const SortDescription &sortDescription = SortDescription());
  592
+  bool GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idActor=-1, int idDirector=-1, int idStudio=-1, int idTag=-1, const SortDescription &sortDescription = SortDescription());
593 593
   bool GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor=-1, int idDirector=-1, int idGenre=-1, int idYear=-1, int idShow=-1);
594 594
   bool GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idActor=-1, int idDirector=-1, int idShow=-1, int idSeason=-1, const SortDescription &sortDescription = SortDescription());
595  
-  bool GetMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idArtist=-1, int idDirector=-1, int idStudio=-1, int idAlbum=-1, const SortDescription &sortDescription = SortDescription());
  595
+  bool GetMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idArtist=-1, int idDirector=-1, int idStudio=-1, int idAlbum=-1, int idTag=-1, const SortDescription &sortDescription = SortDescription());
596 596
   
597 597
   bool GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit=0);
598 598
   bool GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit=0);
119  xbmc/video/windows/GUIWindowVideoNav.cpp
@@ -227,6 +227,8 @@ CStdString CGUIWindowVideoNav::GetQuickpathName(const CStdString& strPath) const
227 227
     return "TvShowYears";
228 228
   else if (strPath.Equals("videodb://2/4/"))
229 229
     return "TvShowActors";
  230
+  else if (strPath.Equals("videodb://2/9/"))
  231
+    return "TvShowTags";
230 232
   else if (strPath.Equals("videodb://2/"))
231 233
     return "TvShows";
232 234
   else if (strPath.Equals("videodb://3/1/"))
@@ -239,6 +241,8 @@ CStdString CGUIWindowVideoNav::GetQuickpathName(const CStdString& strPath) const
239 241
     return "MusicVideoArtists";
240 242
   else if (strPath.Equals("videodb://3/5/"))
241 243
     return "MusicVideoDirectors";
  244
+  else if (strPath.Equals("videodb://3/9/"))
  245
+    return "MusicVideoTags";
242 246
   else if (strPath.Equals("videodb://3/"))
243 247
     return "MusicVideos";
244 248
   else if (strPath.Equals("videodb://4/"))
@@ -365,9 +369,11 @@ bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemL
365 369
         LoadVideoInfo(items);
366 370
     }
367 371
 
368  
-    if (items.GetPath() == "videodb://1/9/" && !items.Contains("newtag://movie"))
  372
+    CVideoDbUrl videoUrl;
  373
+    if (videoUrl.FromString(items.GetPath()) && items.GetContent() == "tags" &&
  374
+       !items.Contains("newtag://" + videoUrl.GetType()))
369 375
     {
370  
-      CFileItemPtr newTag(new CFileItem("newtag://movie", false));
  376
+      CFileItemPtr newTag(new CFileItem("newtag://" + videoUrl.GetType(), false));
371 377
       newTag->SetLabel(g_localizeStrings.Get(20462));
372 378
       newTag->SetLabelPreformated(true);
373 379
       newTag->SetSpecialSort(SortSpecialOnTop);
@@ -672,22 +678,22 @@ void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem)
672 678
       m_database.DeleteSet(params.GetSetId());
673 679
     }
674 680
   }
675  
-  else if (pItem->GetPath().Left(14).Equals("videodb://1/9/") &&
  681
+  else if (m_vecItems->GetContent() == "tags" &&
676 682
            pItem->GetPath().size() > 14 && pItem->m_bIsFolder)
677 683
   {
678 684
     CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
679 685
     pDialog->SetHeading(432);
680 686
     CStdString strLabel;
681  
-    strLabel.Format(g_localizeStrings.Get(433),pItem->GetLabel());
  687
+    strLabel.Format(g_localizeStrings.Get(433), pItem->GetLabel());
682 688
     pDialog->SetLine(1, strLabel);
683  
-    pDialog->SetLine(2, "");;
  689
+    pDialog->SetLine(2, "");
684 690
     pDialog->DoModal();
685 691
     if (pDialog->IsConfirmed())
686 692
     {
687 693
       CVideoDatabaseDirectory dir;
688 694
       CQueryParams params;
689 695
       dir.GetQueryParams(pItem->GetPath(), params);
690  
-      m_database.DeleteTag(params.GetTagId(), "movie");
  696
+      m_database.DeleteTag(params.GetTagId(), (VIDEODB_CONTENT_TYPE)params.GetContentType());
691 697
     }
692 698
   }
693 699
   else if (m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
@@ -966,13 +972,19 @@ void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &butt
966 972
           buttons.Add(CONTEXT_BUTTON_DELETE, 646);
967 973
         }
968 974
 
969  
-        if (item->GetPath().Left(14).Equals("videodb://1/9/") && item->GetPath().size() > 14 && item->m_bIsFolder) // tags
  975
+        if (m_vecItems->GetContent() == "tags" && item->GetPath().size() > 14 && item->m_bIsFolder) // tags
970 976
         {
971  
-          CStdString strLabelAdd; strLabelAdd.Format(g_localizeStrings.Get(20460), g_localizeStrings.Get(20342).c_str());
972  
-          CStdString strLabelRemove; strLabelRemove.Format(g_localizeStrings.Get(20461), g_localizeStrings.Get(20342).c_str());
973  
-          buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
974  
-          buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
975  
-          buttons.Add(CONTEXT_BUTTON_DELETE, 646);
  977
+          CVideoDbUrl videoUrl;
  978
+          if (videoUrl.FromString(item->GetPath()))
  979
+          {
  980
+            std::string mediaType = videoUrl.GetItemType();
  981
+
  982
+            CStdString strLabelAdd; strLabelAdd.Format(g_localizeStrings.Get(20460), GetLocalizedType(videoUrl.GetItemType()).c_str());
  983
+            CStdString strLabelRemove; strLabelRemove.Format(g_localizeStrings.Get(20461), GetLocalizedType(videoUrl.GetItemType()).c_str());
  984
+            buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
  985
+            buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
  986
+            buttons.Add(CONTEXT_BUTTON_DELETE, 646);
  987
+          }
976 988
         }
977 989
 
978 990
         if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
@@ -1248,14 +1260,12 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1248 1260
     }
1249 1261
   case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1250 1262
     {
1251  
-      if (!item->GetPath().Left(10).Equals("videodb://"))
  1263
+      CVideoDbUrl videoUrl;
  1264
+      if (!videoUrl.FromString(item->GetPath()))
1252 1265
         return false;
1253 1266
       
1254  
-      std::string mediaType;
1255  
-      if (item->GetPath().Mid(9, 3).Equals("/1/"))
1256  
-        mediaType = "movie";
1257  
-      else
1258  
-        return false;
  1267
+      std::string mediaType = videoUrl.GetItemType();
  1268
+      mediaType = mediaType.substr(0, mediaType.length() - 1);
1259 1269
 
1260 1270
       CFileItemList items;
1261 1271
       CStdString localizedType = GetLocalizedType(mediaType);
@@ -1282,14 +1292,12 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1282 1292
     }
1283 1293
   case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1284 1294
     {
1285  
-      if (!item->GetPath().Left(10).Equals("videodb://"))
  1295
+      CVideoDbUrl videoUrl;
  1296
+      if (!videoUrl.FromString(item->GetPath()))
1286 1297
         return false;
1287 1298
       
1288  
-      std::string mediaType;
1289  
-      if (item->GetPath().Mid(9, 3).Equals("/1/"))
1290  
-        mediaType = "movie";
1291  
-      else
1292  
-        return false;
  1299
+      std::string mediaType = videoUrl.GetItemType();
  1300
+      mediaType = mediaType.substr(0, mediaType.length() - 1);
1293 1301
 
1294 1302
       CFileItemList items;
1295 1303
       CStdString localizedType = GetLocalizedType(mediaType);
@@ -1469,7 +1477,9 @@ bool CGUIWindowVideoNav::OnClick(int iItem)
1469 1477
     if (!videodb.Open())
1470 1478
       return true;
1471 1479
 
  1480
+    // get the media type and convert from plural to singular (by removing the trailing "s")
1472 1481
     CStdString mediaType = item->GetPath().Mid(9);
  1482
+    mediaType = mediaType.Left(mediaType.size() - 1);
1473 1483
     CStdString localizedType = GetLocalizedType(mediaType);
1474 1484
     if (localizedType.empty())
1475 1485
       return true;
@@ -1477,7 +1487,7 @@ bool CGUIWindowVideoNav::OnClick(int iItem)
1477 1487
     if (!videodb.GetSingleValue("tag", "tag.idTag", videodb.PrepareSQL("tag.strTag = '%s' AND tag.idTag IN (SELECT taglinks.idTag FROM taglinks WHERE taglinks.media_type = '%s')", strTag.c_str(), mediaType.c_str())).empty())
1478 1488
     {
1479 1489
       CStdString strError; strError.Format(g_localizeStrings.Get(20463), strTag.c_str());
1480  
-      CGUIDialogOK::ShowAndGetInput(20462, strError, "", "");
  1490
+      CGUIDialogOK::ShowAndGetInput(20462, "", strError, "");
1481 1491
       return true;
1482 1492
     }
1483 1493
 
@@ -1535,6 +1545,8 @@ CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1535 1545
     return "videodb://2/4/";
1536 1546
   else if (dir.Equals("TvShowStudios"))
1537 1547
     return "videodb://2/5/";
  1548
+  else if (dir.Equals("TvShowTags"))
  1549
+    return "videodb://2/9/";
1538 1550
   else if (dir.Equals("TvShows"))
1539 1551
     return "videodb://2/";
1540 1552
   else if (dir.Equals("MusicVideoGenres"))
@@ -1551,6 +1563,8 @@ CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1551 1563
     return "videodb://3/6/";
1552 1564
   else if (dir.Equals("MusicVideoStudios"))
1553 1565
     return "videodb://3/7/";
  1566
+  else if (dir.Equals("MusicVideoTags"))
  1567
+    return "videodb://3/9/";
1554 1568
   else if (dir.Equals("MusicVideos"))
1555 1569
     return "videodb://3/";
1556 1570
   else if (dir.Equals("RecentlyAddedMovies"))
@@ -1635,23 +1649,44 @@ bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std:
1635 1649
   if (!videodb.Open())
1636 1650
     return false;
1637 1651
 
1638  
-  CFileItemList listItems;
1639  
-  bool result = false;
1640  
-  if (idTag <= 0)
1641  
-    result = videodb.GetMoviesNav("videodb://1/2/", listItems);
1642  
-  else
  1652
+  MediaType mediaType;
  1653
+  std::string baseDir = "videodb://";
  1654
+  std::string idColumn;
  1655
+  if (type.compare("movie") == 0)
1643 1656
   {
1644  
-    if (showAll)
1645  
-    {
1646  
-      CVideoDatabase::Filter filter;
1647  
-      filter.where = videodb.PrepareSQL("movieview.idMovie NOT IN (SELECT taglinks.idMedia FROM taglinks WHERE taglinks.idTag = %d AND taglinks.media_type = '%s')", idTag, type.c_str());
1648  
-      result = videodb.GetMoviesByWhere("videodb://1/2/", filter, listItems);
1649  
-    }
  1657
+    mediaType = MediaTypeMovie;
  1658
+    baseDir += "1";
  1659
+    idColumn = "idMovie";
  1660
+  }
  1661
+  else if (type.compare("tvshow") == 0)
  1662
+  {
  1663
+    mediaType = MediaTypeTvShow;
  1664
+    baseDir += "2";
  1665
+    idColumn = "idShow";
  1666
+  }
  1667
+  else if (type.compare("musicvideo") == 0)
  1668
+  {
  1669
+    mediaType = MediaTypeMusicVideo;
  1670
+    baseDir += "3";
  1671
+    idColumn = "idMVideo";
  1672
+  }
  1673
+
  1674
+  baseDir += "/2/";
  1675
+  CVideoDbUrl videoUrl;
  1676
+  if (!videoUrl.FromString(baseDir))
  1677
+    return false;
  1678
+
  1679
+  CVideoDatabase::Filter filter;
  1680
+  if (idTag > 0)
  1681
+  {
  1682
+    if (!showAll)
  1683
+      videoUrl.AddOption("tagid", idTag);
1650 1684
     else
1651  
-      result = videodb.GetMoviesNav("videodb://1/9/", listItems, -1, -1, -1, -1, -1, -1, -1, idTag);
  1685
+      filter.where = videodb.PrepareSQL("%sview.%s NOT IN (SELECT taglinks.idMedia FROM taglinks WHERE taglinks.idTag = %d AND taglinks.media_type = '%s')", type.c_str(), idColumn.c_str(), idTag, type.c_str());
1652 1686
   }
1653 1687
 
1654  
-  if (!result || listItems.Size() <= 0)
  1688
+  CFileItemList listItems;
  1689
+  if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1655 1690
     return false;
1656 1691
 
1657 1692
   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
@@ -1673,13 +1708,13 @@ bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std:
1673 1708
 
1674 1709
 CStdString CGUIWindowVideoNav::GetLocalizedType(const std::string &strType)
1675 1710
 {
1676  
-  if (strType == "movie")
  1711
+  if (strType == "movie" || strType == "movies")
1677 1712
     return g_localizeStrings.Get(20342);
1678  
-  else if (strType == "tvshow")
  1713
+  else if (strType == "tvshow" || strType == "tvshows")
1679 1714
     return g_localizeStrings.Get(20343);
1680  
-  else if (strType == "episode")
  1715
+  else if (strType == "episode" || strType == "episodes")
1681 1716
     return g_localizeStrings.Get(20359);
1682  
-  else if (strType == "musicvideo")
  1717
+  else if (strType == "musicvideo" || strType == "musicvideos")
1683 1718
     return g_localizeStrings.Get(20391);
1684 1719
   else
1685 1720
     return "";
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.