2015-05-30, 21:07
I have long wanted to sort recorded "box sets" by series/episode so I can see which is the next one in the series to watch
(Particularly a problem when they are transmitted out of order as CBS Action did with the original star trek series recently).
The pvr.mythtv thread suggested mythtv doesn't have season/episode information, but I've discovered it does ... sort of:
As the myth services API already includes series/episode fields, I created a patch for pvr.mythtv to add this information to the recordings folder. Adding the recordings folder as a video source now makes most of my box sets appear in 'TV SHOWS' .
Given that series/episode might be unavailable or unreliable for some users (at least until they tweak the mythtv data using MySQL) I added an option to turn it on and off (default=off) via the add-in setup menu.
This patch is against 1.12.18 (helix branch) and has been tested against my 0.27 mythtv server. Only one language translation for the menu title so far I'm afraid...
Also, here is the SQL I use to populate Season/Episode from the tv_grab_uk_rt guide data. I run this nightly with mythfilldatabase.
If you want to try it, I suggest investigating the content of your recorded and recordedprogram tables in detail before applying it to your system. This is particularly the case if you are using a different listings grabber! It certainly won't work for everyone.
Any thoughts?
(Particularly a problem when they are transmitted out of order as CBS Action did with the original star trek series recently).
The pvr.mythtv thread suggested mythtv doesn't have season/episode information, but I've discovered it does ... sort of:
- Myth's metadata lookup job was already retrieving season/episode for approx. 75% of my recorded 'box set' files.
- If the grabber provides it (I use tv_grab_uk_rt which does) syndicatedepisodenumber in the recordedprogram table has season/episode information, although creative SQL-ing is required to populate the season/episode fields (see below).
- I've seen it suggested that myth 0.28 can also get season/episode from the on-air guide (if your source has it in a sensible format), but I haven't tried this myself...
As the myth services API already includes series/episode fields, I created a patch for pvr.mythtv to add this information to the recordings folder. Adding the recordings folder as a video source now makes most of my box sets appear in 'TV SHOWS' .
Given that series/episode might be unavailable or unreliable for some users (at least until they tweak the mythtv data using MySQL) I added an option to turn it on and off (default=off) via the add-in setup menu.
This patch is against 1.12.18 (helix branch) and has been tested against my 0.27 mythtv server. Only one language translation for the menu title so far I'm afraid...
Code:
diff --git a/pvr.mythtv/resources/language/English (Australia)/strings.po b/pvr.mythtv/resources/language/English (Australia)/strings.po
index 1a90ac5..33c0e61 100644
--- a/pvr.mythtv/resources/language/English (Australia)/strings.po
+++ b/pvr.mythtv/resources/language/English (Australia)/strings.po
@@ -192,6 +192,10 @@ msgctxt "#30064"
msgid "Enable recording fanart/thumbnails"
msgstr "Enable recording fanart/thumbnails"
+msgctxt "#30065"
+msgid "Season/Episode in title where available"
+msgstr "Season/Episode in title where available"
+
msgctxt "#30100"
msgid "Protocol version: %i - Database version: %i"
msgstr "Protocol version: %i - Database version: %i"
diff --git a/pvr.mythtv/resources/language/English (New Zealand)/strings.po b/pvr.mythtv/resources/language/English (New Zealand)/strings.po
index 273f681..ce2ea45 100644
--- a/pvr.mythtv/resources/language/English (New Zealand)/strings.po
+++ b/pvr.mythtv/resources/language/English (New Zealand)/strings.po
@@ -192,6 +192,10 @@ msgctxt "#30064"
msgid "Enable recording fanart/thumbnails"
msgstr "Enable recording fanart/thumbnails"
+msgctxt "#30065"
+msgid "Season/Episode in title where available"
+msgstr "Season/Episode in title where available"
+
msgctxt "#30100"
msgid "Protocol version: %i - Database version: %i"
msgstr "Protocol version: %i - Database version: %i"
diff --git a/pvr.mythtv/resources/language/English (US)/strings.po b/pvr.mythtv/resources/language/English (US)/strings.po
index 3be956b..da38347 100644
--- a/pvr.mythtv/resources/language/English (US)/strings.po
+++ b/pvr.mythtv/resources/language/English (US)/strings.po
@@ -192,6 +192,10 @@ msgctxt "#30064"
msgid "Enable recording fanart/thumbnails"
msgstr "Enable recording fanart/thumbnails"
+msgctxt "#30065"
+msgid "Season/Episode in title where available"
+msgstr "Season/Episode in title where available"
+
msgctxt "#30100"
msgid "Protocol version: %i - Database version: %i"
msgstr "Protocol version: %i - Database version: %i"
diff --git a/pvr.mythtv/resources/language/English/strings.po b/pvr.mythtv/resources/language/English/strings.po
index 6923c79..ec4e94f 100644
--- a/pvr.mythtv/resources/language/English/strings.po
+++ b/pvr.mythtv/resources/language/English/strings.po
@@ -201,7 +201,11 @@ msgctxt "#30064"
msgid "Enable recording fanart/thumbnails"
msgstr ""
-# empty strings from id 30065 to 30099
+msgctxt "#30065"
+msgid "Season/Episode in title where available"
+msgstr ""
+
+# empty strings from id 30066 to 30099
# Systeminformation labels
msgctxt "#30100"
diff --git a/pvr.mythtv/resources/language/German/strings.po b/pvr.mythtv/resources/language/German/strings.po
index 539d6ff..512f9e6 100644
--- a/pvr.mythtv/resources/language/German/strings.po
+++ b/pvr.mythtv/resources/language/German/strings.po
@@ -192,6 +192,10 @@ msgctxt "#30064"
msgid "Enable recording fanart/thumbnails"
msgstr "Aufnahme von Fankunst/Vorschaubilder aktivieren"
+msgctxt "#30065"
+msgid "Season/Episode in title where available"
+msgstr "Saison/Episode in Titel, wo verfügbar"
+
msgctxt "#30100"
msgid "Protocol version: %i - Database version: %i"
msgstr "Protokollversion: %i - Datenbankversion: %i"
diff --git a/pvr.mythtv/resources/settings.xml b/pvr.mythtv/resources/settings.xml
index 425fdc3..ef3f2d7 100644
--- a/pvr.mythtv/resources/settings.xml
+++ b/pvr.mythtv/resources/settings.xml
@@ -29,6 +29,7 @@
<setting id="block_shutdown" type="bool" label="30062" default="true" />
<setting id="tunedelay" type="slider" option="int" range="5,1,30" label="30053" />
<setting id="group_recordings" type="enum" label="30054" lvalues="30055|30056|30057" default="0" />
+ <setting id="include_series_episode" type="bool" label="30065" default="false" />
<setting id="enable_edl" type="enum" label="30058" lvalues="30059|30060|30061" default="0" />
<setting id="channel_icons" type="bool" label="30063" default="true" />
<setting id="recording_icons" type="bool" label="30064" default="true" />
diff --git a/src/client.cpp b/src/client.cpp
index cb78cba..f1a8d87 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -55,6 +55,7 @@ int g_iRecTranscoder = 0;
bool g_bDemuxing = DEFAULT_HANDLE_DEMUXING;
int g_iTuneDelay = DEFAULT_TUNE_DELAY;
int g_iGroupRecordings = GROUP_RECORDINGS_ALWAYS;
+bool g_bIncludeSeriesEpisode = DEFAULT_INCLUDE_SERIES_EPISODE;
int g_iEnableEDL = ENABLE_EDL_ALWAYS;
bool g_bBlockMythShutdown = DEFAULT_BLOCK_SHUTDOWN;
@@ -269,6 +270,14 @@ ADDON_STATUS ADDON_Create(void *hdl, void *props)
g_iGroupRecordings = GROUP_RECORDINGS_ALWAYS;
}
+ /* Read settings "include_series_episode" from settings.xml */
+ if (!XBMC->GetSetting("include_series_episode", &g_bIncludeSeriesEpisode))
+ {
+ /* If setting is unknown fallback to defaults */
+ XBMC->Log(LOG_ERROR, "Couldn't get 'include_series_episode' setting, falling back to '%b' as default", DEFAULT_INCLUDE_SERIES_EPISODE);
+ g_bIncludeSeriesEpisode = DEFAULT_INCLUDE_SERIES_EPISODE;
+ }
+
/* Read setting "enable_edl" from settings.xml */
if (!XBMC->GetSetting("enable_edl", &g_iEnableEDL))
{
@@ -616,6 +625,14 @@ ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
PVR->TriggerRecordingUpdate();
}
}
+ else if (str == "include_series_episode")
+ {
+ XBMC->Log(LOG_INFO, "Changed Setting 'include_series_episode' from %u to %u", g_bIncludeSeriesEpisode, *(bool*)settingValue);
+ if (g_bIncludeSeriesEpisode != *(bool*)settingValue)
+ g_bIncludeSeriesEpisode = *(bool*)settingValue;
+ PVR->TriggerRecordingUpdate();
+ }
+
else if (str == "enable_edl")
{
XBMC->Log(LOG_INFO, "Changed Setting 'enable_edl' from %u to %u", g_iEnableEDL, *(int*)settingValue);
diff --git a/src/client.h b/src/client.h
index a6bb5a5..0de0392 100644
--- a/src/client.h
+++ b/src/client.h
@@ -63,6 +63,7 @@
#define GROUP_RECORDINGS_ALWAYS 0
#define GROUP_RECORDINGS_ONLY_FOR_SERIES 1
#define GROUP_RECORDINGS_NEVER 2
+#define DEFAULT_INCLUDE_SERIES_EPISODE false
#define ENABLE_EDL_ALWAYS 0
#define ENABLE_EDL_DIALOG 1
#define ENABLE_EDL_NEVER 2
@@ -106,9 +107,11 @@ extern bool g_bRecAutoRunJob3;
extern bool g_bRecAutoRunJob4;
extern bool g_bRecAutoExpire;
extern int g_iRecTranscoder;
+///* Advanced Settings
extern bool g_bDemuxing;
extern int g_iTuneDelay;
extern int g_iGroupRecordings;
+extern bool g_bIncludeSeriesEpisode;
extern int g_iEnableEDL;
extern bool g_bBlockMythShutdown;
diff --git a/src/cppmyth/MythProgramInfo.cpp b/src/cppmyth/MythProgramInfo.cpp
index 3b386bc..18634f9 100644
--- a/src/cppmyth/MythProgramInfo.cpp
+++ b/src/cppmyth/MythProgramInfo.cpp
@@ -312,3 +312,8 @@ uint16_t MythProgramInfo::Season() const
{
return (m_proginfo ? m_proginfo->season : 0);
}
+
+uint16_t MythProgramInfo::Episode() const
+{
+ return (m_proginfo ? m_proginfo->episode : 0);
+}
diff --git a/src/cppmyth/MythProgramInfo.h b/src/cppmyth/MythProgramInfo.h
index c704b8c..e010145 100644
--- a/src/cppmyth/MythProgramInfo.h
+++ b/src/cppmyth/MythProgramInfo.h
@@ -80,6 +80,7 @@ public:
std::string StorageGroup() const;
std::string Inetref() const;
uint16_t Season() const;
+ uint16_t Episode() const;
private:
Myth::ProgramPtr m_proginfo;
diff --git a/src/pvrclient-mythtv.cpp b/src/pvrclient-mythtv.cpp
index 54b5e5b..64105e3 100644
--- a/src/pvrclient-mythtv.cpp
+++ b/src/pvrclient-mythtv.cpp
@@ -28,6 +28,7 @@
#include <time.h>
#include <set>
+#include <sstream>
using namespace ADDON;
using namespace PLATFORM;
@@ -808,10 +809,7 @@ PVR_ERROR PVRClientMythTV::GetRecordings(ADDON_HANDLE handle)
//@TODO: tag.iLastPlayedPosition
std::string id = it->second.UID();
- std::string title = MakeProgramTitle(it->second.Title(), it->second.Subtitle());
-
PVR_STRCPY(tag.strRecordingId, id.c_str());
- PVR_STRCPY(tag.strTitle, title.c_str());
PVR_STRCPY(tag.strPlot, it->second.Description().c_str());
PVR_STRCPY(tag.strChannelName, it->second.ChannelName().c_str());
@@ -821,8 +819,33 @@ PVR_ERROR PVRClientMythTV::GetRecordings(ADDON_HANDLE handle)
// Add recording title to directory to group everything according to its name just like MythTV does
std::string strDirectory(it->second.RecordingGroup());
+ // Assign title to the file name only if it isn't in a containing folder and add season/episode if known
+ // Improves ttvdb scraper support, see http://kodi.wiki/view/Naming_video_files/TV_shows
+ std::string title;
+ std::string seasonEpisode = MakeSeasonEpisode(it->second.Season(), it->second.Episode());
if (g_iGroupRecordings == GROUP_RECORDINGS_ALWAYS || (g_iGroupRecordings == GROUP_RECORDINGS_ONLY_FOR_SERIES && it->second.GetPropsSerie()))
+ {
strDirectory.append("/").append(it->second.Title());
+ if (!seasonEpisode.empty() and g_bIncludeSeriesEpisode)
+ {
+ // Where a recording appears in a directory and has a season and episode, include it in place of the title
+ if (it->second.Subtitle().empty()) title = MakeProgramTitle(it->second.Title(), seasonEpisode);
+ else title = MakeProgramTitle(seasonEpisode, it->second.Subtitle());
+ XBMC->Log(LOG_DEBUG, "%s: Season/episode %s inserted in %s (%s)", __FUNCTION__,seasonEpisode.c_str(),title.c_str()),it->second.Title().c_str();
+ }
+ }
+ else
+ {
+ if (!seasonEpisode.empty() and g_bIncludeSeriesEpisode)
+ {
+ // no containing directory so include the season and episode between title and subtitle for 'by filename' sorting in recordings
+ title = MakeProgramTitle( it->second.Title(), MakeProgramTitle(seasonEpisode, it->second.Subtitle()) );
+ XBMC->Log(LOG_DEBUG, "%s: Season/episode %s inserted in %s", __FUNCTION__,seasonEpisode.c_str(),title.c_str());
+ }
+ }
+ //if title hasn's been set fall back to the standard behaviour
+ if (title.empty()) title = MakeProgramTitle(it->second.Title(), it->second.Subtitle());
+ PVR_STRCPY(tag.strTitle, title.c_str());
PVR_STRCPY(tag.strDirectory, strDirectory.c_str());
// Images
@@ -2223,6 +2246,22 @@ std::string PVRClientMythTV::MakeProgramTitle(const std::string& title, const st
return epgtitle;
}
+std::string PVRClientMythTV::MakeSeasonEpisode(uint16_t season, uint16_t episode)
+{
+ std::ostringstream convert;
+ //Specials or series with only one season have a season of 0, so we can't exclude them
+ if (episode>0)
+ {
+ convert << "S";
+ if(season<10) convert << "0";
+ convert << season;
+ convert << "E";
+ if(episode<10) convert << "0";
+ convert << episode;
+ }
+ return convert.str();
+}
+
// Broacast ID is 32 bits integer and allows to identify a EPG item.
// MythTV backend doesn't provide one. So we make it encoding time and channel
// as below:
diff --git a/src/pvrclient-mythtv.h b/src/pvrclient-mythtv.h
index 13d68dc..4342a76 100644
--- a/src/pvrclient-mythtv.h
+++ b/src/pvrclient-mythtv.h
@@ -193,6 +193,7 @@ private:
* \see class MythProgramInfo , class MythEPGInfo
*/
static std::string MakeProgramTitle(const std::string& title, const std::string& subtitle);
+ static std::string MakeSeasonEpisode(uint16_t season, uint16_t episode);
/**
*
Also, here is the SQL I use to populate Season/Episode from the tv_grab_uk_rt guide data. I run this nightly with mythfilldatabase.
If you want to try it, I suggest investigating the content of your recorded and recordedprogram tables in detail before applying it to your system. This is particularly the case if you are using a different listings grabber! It certainly won't work for everyone.
Code:
-- MSQL Query to update season/episode based on UK_RadioTimes xmltvid data format ExxSxx (most series)
update recorded
inner join recordedprogram on
(
recorded.programid=recordedprogram.programid and
recorded.title=recordedprogram.title and
recorded.subtitle=recordedprogram.subtitle
)
set
recorded.season = mid(recordedprogram.syndicatedepisodenumber,locate("S",recordedprogram.syndicatedepisodenumber)+1),
recorded.episode = mid(recordedprogram.syndicatedepisodenumber,2,locate("S",recordedprogram.syndicatedepisodenumber)-2)
where
recorded.title!="" and
recorded.programid!="" and
recordedprogram.syndicatedepisodenumber!="" and
recorded.episode=0 and
recorded.season=0 and
recordedprogram.syndicatedepisodenumber like "E%S%";
-- MSQL Query to update season/episode based on UK_RadioTimes xmltvid data format Exxxx (Mostly soaps and specials)
update recorded
inner join recordedprogram on
(
recorded.programid=recordedprogram.programid and
recorded.title=recordedprogram.title and
recorded.subtitle=recordedprogram.subtitle
)
set
recorded.season = 0,
recorded.episode = mid(recordedprogram.syndicatedepisodenumber,2)
where
recorded.title!="" and
recorded.programid!="" and
recordedprogram.syndicatedepisodenumber!="" and
recorded.episode=0 and
recorded.season=0 and
recordedprogram.syndicatedepisodenumber like "E%" and
recordedprogram.syndicatedepisodenumber not like "%S%";
Any thoughts?