diff --git a/language/English/strings.po b/language/English/strings.po
index bc39872e6523e..0023f0f91701f 100644
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -11338,7 +11338,7 @@ msgstr ""
#: system/settings/settings.xml
msgctxt "#21416"
-msgid "Jump to first unwatched TV show season/episode"
+msgid "Select the first unwatched TV show season/episode"
msgstr ""
#: system/settings/settings.xml
@@ -11562,10 +11562,10 @@ msgctxt "#21465"
msgid "Above video"
msgstr ""
-#. Description of setting "Videos -> Library -> Jump to first unwatched TV show season/episode" with label #21416
+#. Description of setting "Videos -> Library -> Select first unwatched TV show season/episode" with label #21416
#: system/settings/settings.xml
msgctxt "#21466"
-msgid "When entering a TV show season or episode view automatically jump to the first unwatched season or episode."
+msgid "When entering a TV show season or episode view automatically select the first unwatched season or episode. [On first entry] the first unwatched item will be selected only when a view is entered for the first time. [Always] the first unwatched item will be selected every time a view is entered."
msgstr ""
#. Filter (media data) from float value to float value
@@ -11593,7 +11593,48 @@ msgctxt "#21470"
msgid "%s [%d]"
msgstr ""
-#empty strings from id 21471 to 21601
+#. One of the values valid for "Videos -> Library -> Select first unwatched TV show season/episode" with label #21416
+#: system/settings/settings.xml
+msgctxt "#21471"
+msgid "On first entry"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#21472"
+msgid "Include All Seasons and Specials"
+msgstr ""
+
+#. Description of setting "Videos -> Library -> Include All Seasons and Specials" with label #21472
+#: system/settings/settings.xml
+msgctxt "#21473"
+msgid "Whether or not to consider All Seasons and Special items in the unwatched item selection."
+msgstr ""
+
+#. One of the values valid for "Videos -> Library -> Include All Seasons and Specials" with label #21472
+#: system/settings/settings.xml
+msgctxt "#21474"
+msgid "Neither"
+msgstr ""
+
+#. One of the values valid for "Videos -> Library -> Include All Seasons and Specials" with label #21472
+#: system/settings/settings.xml
+msgctxt "#21475"
+msgid "Both"
+msgstr ""
+
+#. One of the values valid for "Videos -> Library -> Include All Seasons and Specials" with label #21472
+#: system/settings/settings.xml
+msgctxt "#21476"
+msgid "Just All Seasons"
+msgstr ""
+
+#. One of the values valid for "Videos -> Library -> Include All Seasons and Specials" with label #21472
+#: system/settings/settings.xml
+msgctxt "#21477"
+msgid "Just Specials"
+msgstr ""
+
+#empty strings from id 21478 to 21601
#: xbmc/Util.cpp
msgctxt "#21602"
@@ -16346,4 +16387,4 @@ msgstr ""
#: system/settings/settings.xml
msgctxt "#38012"
msgid "Hide "All Items" entry in directory (for example All Albums or All Seasons)"
-msgstr ""
\ No newline at end of file
+msgstr ""
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index 12a57652de603..beb316f257258 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -349,10 +349,33 @@
-
+ 1
- false
-
+ 0
+
+
+
+
+
+
+
+
+
+
+ 2
+ 0
+
+
+
+
+
+
+
+
+
+ 0
+
+ 2
diff --git a/xbmc/video/GUIViewStateVideo.cpp b/xbmc/video/GUIViewStateVideo.cpp
index 2d4a3fc9adbb9..21cc661a8ef2d 100644
--- a/xbmc/video/GUIViewStateVideo.cpp
+++ b/xbmc/video/GUIViewStateVideo.cpp
@@ -436,31 +436,6 @@ bool CGUIViewStateWindowVideoNav::AutoPlayNextItem()
return CSettings::Get().GetBool("videoplayer.autoplaynextitem");
}
-bool CGUIViewStateWindowVideoNav::JumpToFirstUnplayedItem()
-{
- if (m_items.IsVideoDb())
- {
- NODE_TYPE NodeType = CVideoDatabaseDirectory::GetDirectoryChildType(m_items.GetPath());
- switch (NodeType)
- {
- case NODE_TYPE_EPISODES:
- if (GetSortMethod().sortBy == SortBy::SortByEpisodeNumber)
- return CSettings::Get().GetBool("videolibrary.jumptofirstunplayeditem");
- else
- return false;
-
- case NODE_TYPE_SEASONS:
- return CSettings::Get().GetBool("videolibrary.jumptofirstunplayeditem");
-
- default:
- return false;
- break;
- }
- }
-
- return CGUIViewStateWindowVideo::JumpToFirstUnplayedItem();
-}
-
CGUIViewStateWindowVideoPlaylist::CGUIViewStateWindowVideoPlaylist(const CFileItemList& items) : CGUIViewStateWindowVideo(items)
{
AddSortMethod(SortByNone, 551, LABEL_MASKS("%L", "", "%L", "")); // Label, empty | Label, empty
diff --git a/xbmc/video/GUIViewStateVideo.h b/xbmc/video/GUIViewStateVideo.h
index 532e59aa6ffdd..31d7dba5cdc4d 100644
--- a/xbmc/video/GUIViewStateVideo.h
+++ b/xbmc/video/GUIViewStateVideo.h
@@ -49,7 +49,6 @@ class CGUIViewStateWindowVideoNav : public CGUIViewStateWindowVideo
public:
CGUIViewStateWindowVideoNav(const CFileItemList& items);
virtual bool AutoPlayNextItem();
- virtual bool JumpToFirstUnplayedItem();
protected:
virtual void SaveViewState();
diff --git a/xbmc/video/windows/GUIWindowVideoNav.cpp b/xbmc/video/windows/GUIWindowVideoNav.cpp
index bf60f81d63a00..118ed43471889 100644
--- a/xbmc/video/windows/GUIWindowVideoNav.cpp
+++ b/xbmc/video/windows/GUIWindowVideoNav.cpp
@@ -269,6 +269,114 @@ std::string CGUIWindowVideoNav::GetQuickpathName(const std::string& strPath) con
}
}
+SelectFirstUnwatchedItem CGUIWindowVideoNav::GetSettingSelectFirstUnwatchedItem()
+{
+ if (m_vecItems->IsVideoDb())
+ {
+ NODE_TYPE nodeType = CVideoDatabaseDirectory::GetDirectoryChildType(m_vecItems->GetPath());
+
+ if (nodeType == NODE_TYPE_SEASONS || nodeType == NODE_TYPE_EPISODES)
+ {
+ int iValue = CSettings::Get().GetInt("videolibrary.tvshowsselectfirstunwatcheditem");
+ if (iValue >= SelectFirstUnwatchedItem::NEVER && iValue <= SelectFirstUnwatchedItem::ALWAYS)
+ return (SelectFirstUnwatchedItem)iValue;
+ }
+ }
+
+ return SelectFirstUnwatchedItem::NEVER;
+}
+
+IncludeAllSeasonsAndSpecials CGUIWindowVideoNav::GetSettingIncludeAllSeasonsAndSpecials()
+{
+ int iValue = CSettings::Get().GetInt("videolibrary.tvshowsincludeallseasonsandspecials");
+ if (iValue >= IncludeAllSeasonsAndSpecials::NEITHER && iValue <= IncludeAllSeasonsAndSpecials::SPECIALS)
+ return (IncludeAllSeasonsAndSpecials)iValue;
+
+ return IncludeAllSeasonsAndSpecials::NEITHER;
+}
+
+int CGUIWindowVideoNav::GetFirstUnwatchedItemIndex(bool includeAllSeasons, bool includeSpecials)
+{
+ int iIndex = 0;
+ int iUnwatchedSeason = INT_MAX;
+
+ // Run through the list of items and find the season number of the first season with unwatched episodes
+ for (int i = 0; i < m_vecItems->Size(); ++i)
+ {
+ CFileItemPtr pItem = m_vecItems->Get(i);
+ if (pItem->IsParentFolder() || !pItem->HasVideoInfoTag())
+ continue;
+
+ CVideoInfoTag *pTag = pItem->GetVideoInfoTag();
+
+ if ((!includeAllSeasons && pTag->m_iSeason < 0) || (!includeSpecials && pTag->m_iSeason == 0))
+ continue;
+
+ // Is the season unwatched, and is its season number lower than the currently identified
+ // first unwatched season
+ if (pTag->m_playCount == 0 && pTag->m_iSeason < iUnwatchedSeason)
+ {
+ iUnwatchedSeason = pTag->m_iSeason;
+ iIndex = i;
+ }
+ }
+
+ NODE_TYPE nodeType = CVideoDatabaseDirectory::GetDirectoryChildType(m_vecItems->GetPath());
+ if (nodeType == NODE_TYPE::NODE_TYPE_EPISODES)
+ {
+ iIndex = 0;
+ int iUnwatchedEpisode = INT_MAX;
+
+ // Now run through the list of items and check episodes from the season identified above
+ // to find the first (lowest episode number) unwatched epsisode.
+ for (int i = 0; i < m_vecItems->Size(); ++i)
+ {
+ CFileItemPtr pItem = m_vecItems->Get(i);
+ if (pItem->IsParentFolder() || !pItem->HasVideoInfoTag())
+ continue;
+
+ CVideoInfoTag *pTag = pItem->GetVideoInfoTag();
+
+ // Does the episode belong to the unwatched season and Is the episode unwatched, and is its epsiode number
+ // lower than the currently identified first unwatched episode
+ if (pTag->m_iSeason == iUnwatchedSeason && pTag->m_playCount == 0 && pTag->m_iEpisode < iUnwatchedEpisode)
+ {
+ iUnwatchedEpisode = pTag->m_iEpisode;
+ iIndex = i;
+ }
+ }
+ }
+
+ return iIndex;
+}
+
+bool CGUIWindowVideoNav::Update(const std::string &strDirectory, bool updateFilterPath /* = true */)
+{
+ if (!CGUIWindowVideoBase::Update(strDirectory, updateFilterPath))
+ return false;
+
+ // Check if we should select the first unwatched item
+ SelectFirstUnwatchedItem selectFirstUnwatched = GetSettingSelectFirstUnwatchedItem();
+ if (selectFirstUnwatched != SelectFirstUnwatchedItem::NEVER)
+ {
+ bool bIsItemSelected = (m_viewControl.GetSelectedItem() > 0);
+
+ if (selectFirstUnwatched == SelectFirstUnwatchedItem::ALWAYS ||
+ (selectFirstUnwatched == SelectFirstUnwatchedItem::ON_FIRST_ENTRY && !bIsItemSelected))
+ {
+ IncludeAllSeasonsAndSpecials incAllSeasonsSpecials = GetSettingIncludeAllSeasonsAndSpecials();
+
+ bool bIncludeAllSeasons = (incAllSeasonsSpecials == IncludeAllSeasonsAndSpecials::BOTH || incAllSeasonsSpecials == IncludeAllSeasonsAndSpecials::ALL_SEASONS);
+ bool bIncludeSpecials = (incAllSeasonsSpecials == IncludeAllSeasonsAndSpecials::BOTH || incAllSeasonsSpecials == IncludeAllSeasonsAndSpecials::SPECIALS);
+
+ int iIndex = GetFirstUnwatchedItemIndex(bIncludeAllSeasons, bIncludeSpecials);
+ m_viewControl.SetSelectedItem(iIndex);
+ }
+ }
+
+ return true;
+}
+
bool CGUIWindowVideoNav::GetDirectory(const std::string &strDirectory, CFileItemList &items)
{
if (m_thumbLoader.IsLoading())
diff --git a/xbmc/video/windows/GUIWindowVideoNav.h b/xbmc/video/windows/GUIWindowVideoNav.h
index c91c6a652b2a9..f828135546cc2 100644
--- a/xbmc/video/windows/GUIWindowVideoNav.h
+++ b/xbmc/video/windows/GUIWindowVideoNav.h
@@ -24,6 +24,21 @@
class CFileItemList;
+enum SelectFirstUnwatchedItem
+{
+ NEVER = 0,
+ ON_FIRST_ENTRY = 1,
+ ALWAYS = 2
+};
+
+enum IncludeAllSeasonsAndSpecials
+{
+ NEITHER = 0,
+ BOTH = 1,
+ ALL_SEASONS = 2,
+ SPECIALS = 3
+};
+
class CGUIWindowVideoNav : public CGUIWindowVideoBase
{
public:
@@ -55,7 +70,9 @@ class CGUIWindowVideoNav : public CGUIWindowVideoBase
virtual bool GetFilteredItems(const std::string &filter, CFileItemList &items);
virtual void OnItemLoaded(CFileItem* pItem) {};
+
// override base class methods
+ virtual bool Update(const std::string &strDirectory, bool updateFilterPath = true);
virtual bool GetDirectory(const std::string &strDirectory, CFileItemList &items);
virtual void UpdateButtons();
virtual void DoSearch(const std::string& strSearch, CFileItemList& items);
@@ -69,4 +86,9 @@ class CGUIWindowVideoNav : public CGUIWindowVideoBase
virtual std::string GetQuickpathName(const std::string& strPath) const;
VECSOURCES m_shares;
+
+private:
+ virtual SelectFirstUnwatchedItem GetSettingSelectFirstUnwatchedItem();
+ virtual IncludeAllSeasonsAndSpecials GetSettingIncludeAllSeasonsAndSpecials();
+ virtual int GetFirstUnwatchedItemIndex(bool includeAllSeasons, bool includeSpecials);
};
diff --git a/xbmc/view/GUIViewState.cpp b/xbmc/view/GUIViewState.cpp
index e2988a1f9f574..fbc81be19a23a 100644
--- a/xbmc/view/GUIViewState.cpp
+++ b/xbmc/view/GUIViewState.cpp
@@ -395,11 +395,6 @@ bool CGUIViewState::AutoPlayNextItem()
return false;
}
-bool CGUIViewState::JumpToFirstUnplayedItem()
-{
- return false;
-}
-
std::string CGUIViewState::GetLockType()
{
return "";
diff --git a/xbmc/view/GUIViewState.h b/xbmc/view/GUIViewState.h
index 2b207951edbb9..2a6eba6feb8a0 100644
--- a/xbmc/view/GUIViewState.h
+++ b/xbmc/view/GUIViewState.h
@@ -56,7 +56,6 @@ class CGUIViewState
void SetPlaylistDirectory(const std::string& strDirectory);
bool IsCurrentPlaylistDirectory(const std::string& strDirectory);
virtual bool AutoPlayNextItem();
- virtual bool JumpToFirstUnplayedItem();
virtual std::string GetLockType();
virtual std::string GetExtensions();
diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp
index 9d7d5db7bc6e1..52ce0d6cb57ff 100644
--- a/xbmc/windows/GUIMediaWindow.cpp
+++ b/xbmc/windows/GUIMediaWindow.cpp
@@ -853,35 +853,9 @@ bool CGUIMediaWindow::Update(const std::string &strDirectory, bool updateFilterP
}
}
- // if we haven't found the selected item, see if we should select the first unplayed item
- // if yes, find the first unplayed item and select it
- // if no, select the first item
+ // if we haven't found the selected item, select the first item
if (!bSelectedFound)
- {
- int iIndex = 0; // index of the item to select, default to the first item
-
- // Check if we should select the first unplayed item
- if (m_guiState.get()->JumpToFirstUnplayedItem())
- {
- // Find the index of the
- for (int i = 0; i < m_vecItems->Size(); ++i)
- {
- CFileItemPtr pItem = m_vecItems->Get(i);
- // We don't want to jump to the parent folder item or an All Seasons item
- if (pItem->IsParentFolder() || !pItem->HasVideoInfoTag() ||
- (pItem->GetVideoInfoTag()->m_type == MediaTypeSeason && pItem->GetVideoInfoTag()->m_iSeason < 0))
- continue;
-
- if (pItem->GetVideoInfoTag()->m_playCount == 0)
- {
- iIndex = i;
- break;
- }
- }
- }
-
- m_viewControl.SetSelectedItem(iIndex);
- }
+ m_viewControl.SetSelectedItem(0);
m_history.AddPath(m_vecItems->GetPath(), m_strFilterPath);