Skip to content


Play a specific title/playlist for DVD/BR iso/folder #1736

wants to merge 3 commits into from

3 participants


This feature allows a specific title specified either in the nfo or in the filename to be played. This generally corresponds to a movie or episode

The main motivation for the feature is to be able to play episodes from multi-episode discs
It can also be used to play a movie directly from the DVD without going through the menu and it provides an intermediate fix for those people with blurays for which the "longest title" is not the right choice

dragonflight added some commits
@dragonflight dragonflight adds the ability to play a specific title on a DVD or BluRay from fil…

dvdname.titNN.iso will play title NN (playlist NNNNN.mpls on BR)
a ttile in a DVD folder can be played with
DVDname.titNN/VIDEO_TS/VIDEO_TS.IFO in particular a set of links
@dragonflight dragonflight added nfo file entry <playtitle>
        0	play longest title (not yet implemented)
        N play title N (or playlist NNNNN.mpls - leading zeros are uneccessary)
        100000 or absent play menu or disc start
In the case of DVDs the dvdplayer plays just one pgc (normally an episode or movie)
and then quits

Use CURL to parse the URL, then just grab out the URL option directly?

I thought of that, tried it, but something about the way options worked didn't. I will look at it again and either remember why I didn't do it or do it


Because before my changes strPath and strFile were the same, but now strPath is strFile without the optional ?title.
The move to the top is because strPath gets modifed in the middle


perhaps default title to 0 ?

I wanted to differentiate between doing nothing or choosing the longest. in the case of BR they are the same (today), but in the case of DVDs it would be the difference between playing the menu or playing the longest title (which I haven't implemented)


I missed this change. I originally used negative titles to disable my attempt to decide when the title was finished playing, but after playing with this the last month it seems to always (knock on wood) work so I removed it for this submit, so no it shouldn't be negative

Team Kodi member

Ehh.. why are you doing this. this support is already there with other path syntax.. bluray://url encoded path/BDMV/00001.mpls

Team Kodi member

For blurays that is. Something similar should be added for dvd's. You can see the path structure by adding a file source like: bluray://[url encoded path to bluray disk]/

There was plans to auto browse this for blurays. But never got added.


I did in fact notice that as a path option, but I didn't know how to make it scrape (and no one has suggested how in the forums when people are complaining that XBMC chooses the wrong - well not the one they want to see - playlist)
Humourously, this is a case where a Bluray folder instead of an ISO is better.
Out of interest I tried adding a source as you suggested and I got two items
Select from all titles ...
Show bluray menus

The first returns an empty list, the second does nothing (I have no blurays, so I tried this with a bluray that I downloaded, only one mpls, but XBMC plays the iso and gives a list of mpls's if it is a folder, so it should be ok.

This PR gives a relatively understandable way of "fixing" which mpls XBMC plays

When things are changed (or explained it is possible today) to scrape this stuff I agree that a DVD:// syntax should be created.

Team Kodi member

It should not be an empty list, then something is wrong. It should list all available titles on the bluray. What did the log say?

To hook this up into nfo scraping should be simple. You just need to construct an alternate path in the case when a nfo selects a title. Normally xbmc selects the longest title on the bluray.

I will not accept another way of accomplishing the same thing when it comes to bluray. Adding options the way you did will break down on protocols that already need other options like http, ftp.. I would really prefer that you used a similar URL scheme for dvd's. ie dvdnav://[url]/titles/1 to play a single title on the dvd.

Do note that these stuff won't make it into Frodo release as they are feature additions and we are in lock down, so you have time to work on this if it something you want in.


You were right I got the wrong path in the url.

as for using ?title to pass the title info I only did that because there was code to already do it, except property BlurayStartingTitle was never set, nor tested inside DVDInputStreamBluray.cpp. The option only ever exists between DVDPlayer and DVDInputStreamBluray and DVDInputStreamNavigator. I would be better if DVDPlayer could pass options as another argument to the streams. As an alternative I could nest the filename in a bluray:[urlencoded filename]/?title=NN to avoid ambiguity, but I'm not sure XMBC handles nested URLs properly

I currently don't have any BluRays (but I will), but I have lots of DVDs. If I were to use your scheme or urls to play specific titles and use alternate paths I would need 4000+ paths for my tv shows and another 2000+ for my dvds (I have them set to play the movie directly without going through the menus by default) and would be a real maintenance mess. As there already are people with largish BR collections you might want to rethink "I will not accept another way of accomplishing the same thing when it comes to bluray".

Team Kodi member
Team Kodi member

There are 3 separate issues we are talking about

  1. the passing a parameter from DVDPlayer to the DVDInput streams
    I used ?title because it was already there. I am happy to be told how best to pass the info to the input streams.
    e.g. am happy to use bluray:[url]/?title=nn using curl to encode it and decode it in DVDInputStreamBluray/DVDInputStreamNavigate. The option would otherwise never be exposed.

  2. The addition of a filename option that allows a specific title to be played.
    While this is a duplicate of bluray://url encoded path/BDMV/00001.mpls it is scrape-able (scrapable?) and doesn't need potentially thousands of alternate filenames to implement.

  3. The addition of a database field that specifies which title to play set in the nfo (and maybe a gui option at some point)

I use #2 because I have an external tool that I use to generate the episode names an title number semi-automatically. I also have scripts which play the individual episodes in mplayer and xine.

Team Kodi member

1: No difference than 2.

2: The problem with this is that if you point at a bluray iso residing on a http/webdav server (odd case i know). You add a option parameter to the url that will choke the http server serving it since it will think it's something it should parse.

Also I don't see how that would be more scrapable than anything else.. Title in a bluray is just the mpls file. So if you want the 3rd title. it is always BDMV/00003.mlpl. If that is too hard, we could add support for bluray://path/titles/1. Your script just need to be able to url encode.

3: Sure i don't mind it at all. (as long as our system can handle same file being referred for multiple movies, but it probably can, cue sheets aught to be same situation).


1) is different because in DVDPlayer filename becomes bluray:[urlencode(filename)]/?title-nn
then in DVDInput stream gets bluray:[url]/?title=nn and filename = urldecode(url) and title=nn
DVDInputStream just uses filename as it would have before
2) there is no problem with fetching it from a http/webdav server (actually there might be if it ended in a ?title=nn, but that could be handled by always adding the ?title option) because the ?title only exists in between DVDPlayer and DVDInputStream. it is added just before calling DVDInputStream and it is stripped in DVDInputStream before doing anything else.

the problem with scrape-able (as opposed to scrap-able) is that you can't pass the BDMV/00003.mpls if it is an iso (except of course with a url which you can't represent in a filename (though you should be able to in a symlink which didn't seem to work for me and I don't know if it works in Windows regardless) What I did was create a new filetype .titNN.iso which is a unique type interpreted as title NN (or BDMV/NN.mpls) by the player which could even be configured to be played with an external player if one so desired.

Team Kodi member
the problem with scrape-able (as opposed to scrap-able) is that you can't pass the BDMV/00003.mpls if it is an iso (except of course with a url which you can't represent in a filename

This doesn't make sense to me. I must be missing something in regards to what it is you want to accomplish. You can obviously represent it as a path. And can't a .nfo file point out any given path?


No nfo files don't point to paths (at least individual ones as opposed to the whole library dump), it is the other way around.
You need a video file to trigger looking for an nfo (name of the nfo file is derived from the video filename and scraper options). You could add an option to specify a redirected path, and in essence that is what I have done with the playtitle tag.
You can (if the BR is a folder) create symlink of the form SnEnn name.mpls -> BRfolder/BDMV/00003.mpls and place the symlink in a tv show folder, it will be scraped and then you can play episode nn from the library.
In linux one could create a symlink of the form SnEnn name.mpls ->bluray:[url]/iBDMV/00003.mpls
and again the symlink would be scrape-able, but for some reason when I tried it it didn't work, but I'm sure it could be made to work in Linux. As I said I don't know if it could be made to work on windows.

Team Kodi member
Team Kodi member

So i'm getting somewhat closer to an understanding :).

We just had a thought on how to accomplish this. Or at least it should cause much less changes in xbmc. Not sure you think it's equival, but here we go..

We have stuff called .strm files. They are pretty much shortcuts to another file. It's just a text file containing the path to something else (often an online url).

If you add .strm files with ShowTitle.S01E01.strm that point to the .mpls file that signifies the title, i suspect we would be very close to working already. It should scrape meta data properly, and likely start up the movie properly.

(obviously we need a way to start specific titles on dvd's too for them too work).


Let me start by apologizing for not having written a better description to start with. I have been doing this for about 10 years (not with XBMC of course) and I forgot that what is obvious to me is only obvious to me.

It doesn't appear that .strm files would work as the code treats them as playlists (as well as .url), but something similar could , but it would have to introduce the idea of a relative URL encoded path (now xbmc's uses of urls/paths back and forth really confuses me :) ). as one definitely wants the ability to reorganize video in the filesystem.

Let me just say that the new file type (clumsily implemented, but limiting the code affected) and a symlink was intended to accomplish what you are thinking of with the .strm file

To try and correct my initial error with a description of want I want to accomplish. I'm not that familiar with BluRay so some of this may not be strictly correct, but hopefully it will clarify some stuff.


Sorry I didn't realize (obvious now) that git wouldn't word wrap, so ignore first comit

Team Kodi member

Let's say we fix .strm files to support relative paths + some uri scheme to start a standard dvd at a specific title/chapter. Then they aught to solve your issue. I don't follow what it would not solve?

Also we should support the same logic of starting a title from a DVD as we currently do on blurays when you request to start them.


As I said in my README that would work fine except that a .strm is a playlist.
Perhaps .urlalias? as a generic filesystem -> url translation, or whatever.

There is still value in adding the database item, as you would get 2 scraped items the urlfile and the movie.iso, movie.../VIDEO_TS.IFO or movie.../index.bdmv

If I/you decide to implement play longest for DVDs and play menus for blurarys, it would be nice to add "Play longest" as an option along side the "start DVD at main menu" option

Team Kodi member

Fine, I might have chosen to fix .url instead, but since I have not even contemplated moving my music collection to xbmc I'm not sure I would even know where to start fixing it so as not to break the music library (or leave it with one meaning for music and another for video), guidance would be useful.

I was not trying to knock the Bluray implementation (though as I'm sure you realize if it is an iso you elect the longest and if it is a folder you list the PLAYLIST directory by default). I only have the one bluray disc that I downloaded to test this and it may have been stripped of menus. My point was merely that the playTitle nfo option allowed one to decide on one of three options (menu,longest,specific) on a per video basis and this was useful for DVDs (more useful when play longest title is implemented) and for BluRays (more so if it plays menus today)

Team Kodi member

OK, it is immaterial to this topic but I checked what is happening on my ONE BluRay (It has a simple menu that just immediately starts the movie and it has only one playlist.) I monitored the debug log to know what is happening.

I have two files

I scraped them into my library
in library mode (select or play)
secretary.iso - plays longest title
xxx - displays dialog, select bluray menu plays menu

in files mode
secretary.iso (select,play) - plays longest title
xxx (no stacking, play) - plays longest title
xxx (stacking, select or play) displays dialog

Team Kodi member
Team Kodi member

According to the debug log (and the extra message I put in ), when I said play longest it is calling CDVDInputStreamBluray, trying the protection (non-found), then calling CDVDInputStreamBluray::GetTitleLongest() and finally playing udf://%2fxmbclibrary%2fBluRay%2fSecretary.iso%2fBDMV%2fPLAYLIST%2f00001.mpls

Team Kodi member

Its going to be a little hard to fix as the dialog is displayed when the user attempts to play a bluray (as determined by CFileItem::IsBDFile() which merely checks if you are playing index.bdmv in CGUIWindowVideoBase, whereas CDVDPlayer decides to try playing the iso as a bluray if DVDInputStreamNavigator doesn't like it. One of the "oddities" I discovered researching this feature. I just assumed XBMC was waiting for full menu support to just drop the dialog.

Team Kodi member
Team Kodi member

Closing for now. This need to be split between bluray and dvd also display of selection menu is now available for iso files too in the bluray case.

@elupus elupus closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 5, 2012
  1. @dragonflight

    adds the ability to play a specific title on a DVD or BluRay from fil…

    dragonflight committed
    dvdname.titNN.iso will play title NN (playlist NNNNN.mpls on BR)
    a ttile in a DVD folder can be played with
    DVDname.titNN/VIDEO_TS/VIDEO_TS.IFO in particular a set of links
  2. @dragonflight

    added nfo file entry <playtitle>

    dragonflight committed
            0	play longest title (not yet implemented)
            N play title N (or playlist NNNNN.mpls - leading zeros are uneccessary)
            100000 or absent play menu or disc start
    In the case of DVDs the dvdplayer plays just one pgc (normally an episode or movie)
    and then quits
Commits on Nov 8, 2012
  1. @dragonflight
98 PlayTitle.README
@@ -0,0 +1,98 @@
+I like to think of Optical disks having three file systems
+1) a udf (in iso form) or a native file system (in folder form)
+2) a menu driven file system (dvdnav for DVDs) and not yet implemented for
+3) a title/chapter (mpls/?) file system
+and from the user point of view it contains 1 or more movies, episodes,
+"extras" as well as menus (and sometimes extras buried in the menus) and some
+computer mode apps
+I (and some others) like to keep the disc intact as the menus are occasionally
+interesting and occasionally (rarely) the only way to play a movie.
+Fortunately (and why makemkv is useful) most of the times the interesting bits
+constitute one and only one title, though a few DVDS use chapters to index TV
+show episodes
+I would like to propose a mechanism that allows user to select one of three
+1) play the menu ( I like XBMC's option to go straight to the main menu)
+2) play the main title (given the fact that we can't actually tell what it is
+play the longest title)
+3) play a specific video (I have suggested a single title, though after some
+work it should probably be a title/chapter pair)
+Given that I am a slow (and error-prone) typist I would like a mechanism to
+reasonably automatically scrape and assign video information to the videos
+Method 1)
+ Create a database field that selects which video to play and a
+mechanism to specify it in an nfo file.
+ In order to create the nfo file there needs to be some file system file
+that the scanner can scrap.
+ I have found symlinks to be the easiest, though if one isn't interested
+in the "specials" then tvshow/s1e1e2e3.iso works.
+ The only small difficulty I have with this is it is a 3 step process.
+ scan the files
+ edit the nfos
+ re-scan the files
+Method 2)
+ Encode in the file (name or contents) the specification for the video.
+ IF it is encoded in the contents (either as a test file or as a
+symlink) it must contain information relative to either the file or parent
+directory so that the movie/tvshow can be moved around in the physical file
+ This has the advantage that it is easier for an external tool to
+analyze the disc and create the files and they are playable outside of XBMC
+(with appropriate scripts).
+Method 1)
+ I added a database field playTitle which is a single integer N, with
+N=0 meaning to play the longest title, N>=100000 or absent use the menus
+(eventually with BluRays) and otherwise play title N
+ This field is understood by DVDPlayer and passed to the InputStream (by
+appending ?title=N to the filename as pointed out probably better as
+playtitle:[urlencoded filename]/title=N or bluray: and dvdnav: or) though I
+don't believe there can be any other URL with a title option inside the
+filename at this point)
+ This option is interpreted by the InputStream and appropriate action
+Method 2)
+ I essentially created two sets of new file types .TITNN.ISO (for DVD
+and Bluray) and .TITNN (for DVD Folders - for BluRays the user can directly
+access BDMV/NNNNN.mpls or index.bdmv, but they will lose the option to play
+longest). This how I can play them directly in Linux.
+ The .TITNN.ISO is interpreted by DVDPlayer and the .TITNN is
+interpreted by DVDInputStreamNavigator (courtesy of a previous implementation)
+ I did not add the implementation of getting longest title on DVD yet.
+(as it turns out I have a metadata file in my library(file system) that has
+this info so I scraped it and added it to the nfo file)
+One of the issues is that one can't represent .../bluray.iso/BDMV/NNNNN.mpls in
+a file system. One could decide to interpret that as
+bluray://[.../bluray.iso/BDMV/NNNNN.mpls]/BDMV/NNNNN.mpls with the
+understanding that .../bluray.iso/BDMV/NNNNN.mpls is a symlink to or an actual
+.iso (or rars or zips or ...) but it is pretty ugly
+Using a file.url with a text content representing either an absolute or a
+relative URL has some appeal but that is already taken (though maybe it could
+be taken back) and I suspect it might be pretty hard to get right, but I'm sure
+it would have other applications.
+A secondary/scrape-able NFO file say .nfoi (like elupus thought existed). The
+scanner would scrape it as a file and allow it to set filename, pathname,
+playTitle or whatever. There would need to be a database change to record the
+original filename to be able to save the scraped data to a corresponding nfo
25 xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.cpp
@@ -274,13 +274,25 @@ BLURAY_TITLE_INFO* CDVDInputStreamBluray::GetTitleFile(const std::string& filena
bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content)
+ int title = 100000;
if(m_player == NULL)
return false;
+ CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s", strFile);
CStdString strPath(strFile);
CStdString filename;
CStdString root;
+ int iPos = strPath.ReverseFind('?');
+ if( iPos > 0
+ && iPos > strPath.ReverseFind('/')
+ && iPos > strPath.ReverseFind('\\')
+ && strPath.Mid(iPos,7).CompareNoCase("?title") )
+ {
+ title = atoi( strPath.Mid(iPos+7).c_str());
+ strPath = strPath.Left(iPos);
+ }
CURL url(strPath);
@@ -289,6 +301,7 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
+ filename = URIUtils::GetFileName(strPath);
@@ -304,7 +317,6 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
root = strPath;
- filename = URIUtils::GetFileName(strFile);
if (!m_dll)
@@ -315,7 +327,11 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
m_dll->bd_set_debug_mask(DBG_CRIT | DBG_BLURAY | DBG_NAV);
- CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s", root.c_str());
+ if( title == 100000 )
+ CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s, %s", root.c_str(), filename.c_str());
+ else
+ CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s, %s title=%d", root.c_str(), filename.c_str(), title);
m_bd = m_dll->bd_open(root.c_str(), NULL);
@@ -366,7 +382,10 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
m_navmode = false;
- m_title = GetTitleLongest();
+ if( title >= 100000 || title == 0 )
+ m_title = GetTitleLongest();
+ else
+ m_title = m_dll->bd_get_playlist_info(m_bd, abs(title), 0);
else if(URIUtils::GetExtension(filename).Equals(".mpls"))
57 xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp
@@ -52,6 +52,7 @@ CDVDInputStreamNavigator::CDVDInputStreamNavigator(IDVDPlayer* player) : CDVDInp
m_bEOF = false;
m_icurrentGroupId = 0;
m_lastevent = DVDNAV_NOP;
+ m_bFinishedPGC = false;
memset(m_lastblock, 0, sizeof(m_lastblock));
@@ -64,6 +65,7 @@ CDVDInputStreamNavigator::~CDVDInputStreamNavigator()
bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& content)
char* strDVDFile;
+ int start_title = 100000;
m_icurrentGroupId = 0;
if (!CDVDInputStream::Open(strFile, "video/x-dvd-mpeg"))
return false;
@@ -83,6 +85,12 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
// at least one path separator character.
strDVDFile = strdup(strFile);
+ char *p = rindex(strDVDFile, '?');
+ if( p && strncasecmp( p, "?title=", 7) == 0 )
+ {
+ start_title = atoi( p+7 );
+ *p = 0;
+ }
int len = strlen(strDVDFile);
if(len >= 13 // +1 on purpose, to include a separator char before the searched string
@@ -94,6 +102,16 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
&& strncasecmp(strDVDFile + len - 8, "VIDEO_TS", 8) == 0)
strDVDFile[len - 9] = '\0';
+ len = strlen(strDVDFile);
+ if( start_title < 100000 )
+ ;
+ else if(len >= 6 // .TITNN
+ && strncasecmp(strDVDFile + len - 6, ".TIT", 4) == 0)
+ start_title = atoi( strDVDFile + len - 2 );
+ else if(len >= 6 // .TRKNN // historical reasons
+ && strncasecmp(strDVDFile + len - 6, ".TRK", 4) == 0)
+ start_title = atoi( strDVDFile + len - 2 );
#if defined(TARGET_DARWIN_OSX)
// if physical DVDs, libdvdnav wants "/dev/rdiskN" device name for OSX,
// strDVDFile will get realloc'ed and replaced IF this is a physical DVD.
@@ -185,8 +203,29 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
return false;
+ m_iTitle = m_iTitleCount = 0;
+ m_iPart = m_iPartCount = 0;
+ m_iTime = m_iTotalTime = 0;
+ m_iPlayTitle = 0;
+ if( start_title < 100000 )
+ {
+ int len, event, parts;
+ uint8_t buf[2048];
+ uint8_t* buf_ptr = buf;
+ CLog::Log(LOGDEBUG, "*************** setting title to %d", start_title);
+ m_iTitle = start_title;
+ m_iPlayTitle = start_title;
+ m_iPlayTitleTotalTime = 0;
+ // m_dll.dvdnav_part_play(m_dvdnav, 0, 1);
+ m_dll.dvdnav_part_play(m_dvdnav, m_iTitle, 1);
+ // these all get fixed up in DVDNAV_CELL_CHANGE
+ m_iTitleCount = 0;
+ m_iPart = m_iPartCount = 0;
+ m_iTime = m_iTotalTime = 0;
+ }
// jump directly to title menu
- if(g_guiSettings.GetBool("dvds.automenu"))
+ else if(g_guiSettings.GetBool("dvds.automenu"))
int len, event;
uint8_t buf[2048];
@@ -208,9 +247,6 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
m_iVobUnitCorrection = 0LL;
m_bInMenu = false;
m_holdmode = HOLDMODE_NONE;
- m_iTitle = m_iTitleCount = 0;
- m_iPart = m_iPartCount = 0;
- m_iTime = m_iTotalTime = 0;
return true;
@@ -464,9 +500,16 @@ int CDVDInputStreamNavigator::ProcessBlock(BYTE* dest_buffer, int* read)
m_iCellStart = cell_change_event->cell_start; // store cell time as we need that for time later
m_iTime = (int) (m_iCellStart / 90);
m_iTotalTime = (int) (cell_change_event->pgc_length / 90);
+ if( m_iTitle == m_iPlayTitle )
+ m_iPlayTitleTotalTime = m_iTotalTime;
m_icurrentGroupId = cell_change_event->pgN * 1000 + cell_change_event->cellN;
- iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_CELL_CHANGE);
+ if( m_iPlayTitle && m_bFinishedPGC ) {
+ m_bEOF = true;
+ iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_STOP);
+ }
+ else
+ iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_CELL_CHANGE);
@@ -516,6 +559,10 @@ int CDVDInputStreamNavigator::ProcessBlock(BYTE* dest_buffer, int* read)
CLog::Log(LOGDEBUG, "DVDNAV_NAV_PACKET - DISCONTINUITY FROM:%"PRId64" TO:%"PRId64" DIFF:%"PRId64, (m_iVobUnitStop * 1000)/90, ((int64_t)pci->pci_gi.vobu_s_ptm*1000)/90, (gap*1000)/90);
+ if( m_iTitle == m_iPlayTitle && pci->pci_gi.vobu_e_ptm/90 >= m_iTotalTime-1 )
+ {
+ m_bFinishedPGC = true;
+ }
m_iVobUnitStart = pci->pci_gi.vobu_s_ptm;
m_iVobUnitStop = pci->pci_gi.vobu_e_ptm;
4 xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.h
@@ -154,6 +154,7 @@ class CDVDInputStreamNavigator
int m_iTotalTime;
int m_iTime;
+ bool m_bFinishedPGC;
int64_t m_iCellStart; // start time of current cell in pts units (90khz clock)
bool m_bInMenu;
@@ -164,7 +165,8 @@ class CDVDInputStreamNavigator
int m_iTitleCount;
int m_iTitle;
+ int m_iPlayTitle;
+ int m_iPlayTitleTotalTime;
int m_iPartCount;
int m_iPart;
31 xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -557,6 +557,8 @@ bool CDVDPlayer::OpenInputStream()
m_filename = g_mediaManager.TranslateDevicePath("");
+ int title = 100000;
// before creating the input stream, if this is an HLS playlist then get the
// most appropriate bitrate based on our network settings
@@ -579,7 +581,30 @@ bool CDVDPlayer::OpenInputStream()
- if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype))
+ if(title == 100000 && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)) && m_item.HasVideoInfoTag())
+ title = m_item.GetVideoInfoTag()->m_iPlayTitle;
+ if( title >= 100000 ) // match filename.titnnn.iso
+ {
+ int t, n=-1, len;
+ char *p, *q;
+ p = strdup(m_filename.c_str());
+ len = strlen( p );
+ if( len > 4 && strcasecmp(&p[len-4], ".iso" ) == 0 )
+ {
+ p[len-4] = 0;
+ if( (q = rindex(p, '.'))
+ && strcasecmp( q, ".tit" )
+ && sscanf( q+4, "%d%n", &t, &n ) >= 1
+ && q[n+4] == 0 )
+ title = t;
+ }
+ free( p );
+ }
+ if( title < 100000 )
+ filename.AppendFormat("?title=%d",title);
+ if (!m_pInputStream->Open(filename.c_str(), m_mimetype))
@@ -587,10 +612,6 @@ bool CDVDPlayer::OpenInputStream()
m_mimetype = "bluray/iso";
filename = m_filename;
filename = filename + "/BDMV/index.bdmv";
- int title = (int)m_item.GetProperty("BlurayStartingTitle").asInteger();
- if( title )
- filename.AppendFormat("?title=%d",title);
m_filename = filename;
goto retry;
1 xbmc/utils/DatabaseUtils.h
@@ -112,6 +112,7 @@ typedef enum {
+ FieldPlayTitle,
31 xbmc/video/VideoDatabase.cpp
@@ -136,7 +136,7 @@ bool CVideoDatabase::CreateTables()
column.Format(",c%02d text", i);
columns += column;
- columns += ", idSet integer)";
+ columns += ", idSet integer, playTitle integer )";
m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
@@ -205,7 +205,7 @@ bool CVideoDatabase::CreateTables()
columns += column;
- columns += ", idShow integer)";
+ columns += ", idShow integer, playTitle integer)";
m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
@@ -2012,6 +2012,7 @@ int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, con
sql += PrepareSQL(", idSet = %i", idSet);
sql += ", idSet = NULL";
+ sql += PrepareSQL(", playTitle = %i", details.m_iPlayTitle);
sql += PrepareSQL(" where idMovie=%i", idMovie);
@@ -2170,6 +2171,7 @@ int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, c
// and insert the new row
CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
+ sql += PrepareSQL(", playTitle = %i", details.m_iPlayTitle);
sql += PrepareSQL(" where idEpisode=%i", idEpisode);
@@ -3203,10 +3205,11 @@ CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* cons
details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
+ details.m_iPlayTitle = record->at(VIDEODB_DETAILS_MOVIE_PLAY_TITLE).get_asInt();
details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
details.m_resumePoint.type = CBookmark::RESUME;
movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
if (needsCast)
@@ -3320,6 +3323,7 @@ CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* co
details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
+ details.m_iPlayTitle = record->at(VIDEODB_DETAILS_EPISODE_PLAY_TITLE).get_asInt();
details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
@@ -4181,6 +4185,27 @@ bool CVideoDatabase::UpdateOldVersion(int iVersion)
+ if (iVersion < 73)
+ { // add idSet to movie table
+ m_pDS->exec("ALTER TABLE movie ADD playTitle integer");
+ m_pDS->query("SELECT idMovie FROM movie");
+ while (!m_pDS->eof())
+ {
+ int idMovie = m_pDS->fv(0).get_asInt();
+ CStdString sql = PrepareSQL("UPDATE movie SET playTitle=100000 WHERE idMovie = %d", idMovie);
+ m_pDS2->exec(sql.c_str());
+ m_pDS->next();
+ }
+ m_pDS->exec("ALTER TABLE episode ADD playTitle integer");
+ m_pDS->query("SELECT idEpisode FROM episode");
+ while (!m_pDS->eof())
+ {
+ int idEpisode = m_pDS->fv(0).get_asInt();
+ CStdString update = PrepareSQL("UPDATE episode SET playTitle=100000 WHERE idEpisode=%d", idEpisode);
+ m_pDS2->exec(update.c_str());
+ m_pDS->next();
+ }
+ }
// always recreate the view after any table change
return true;
48 xbmc/video/VideoDatabase.h
@@ -70,30 +70,32 @@ namespace VIDEO
@@ -804,7 +806,7 @@ class CVideoDatabase : public CDatabase
bool LookupByFolders(const CStdString &path, bool shows = false);
- virtual int GetMinVersion() const { return 72; };
+ virtual int GetMinVersion() const { return 73; };
virtual int GetExportVersion() const { return 1; };
const char *GetBaseDBName() const { return "MyVideos"; };
10 xbmc/video/VideoInfoTag.cpp
@@ -85,6 +85,7 @@ void CVideoInfoTag::Reset()
m_playCount = 0;
m_fEpBookmark = 0;
+ m_iPlayTitle = 100000;
m_parentPathID = -1;
@@ -115,6 +116,8 @@ bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathIn
XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
XMLUtils::SetFloat(movie, "rating", m_fRating);
XMLUtils::SetFloat(movie, "epbookmark", m_fEpBookmark);
+ if( m_iPlayTitle < 100000 )
+ XMLUtils::SetInt(movie, "playtitle", m_iPlayTitle);
XMLUtils::SetInt(movie, "year", m_iYear);
XMLUtils::SetInt(movie, "top250", m_iTop250);
if (tag == "episodedetails" || tag == "tvshow")
@@ -336,6 +339,7 @@ void CVideoInfoTag::Archive(CArchive& ar)
ar << m_dateAdded.GetAsDBDateTime();
ar << m_type;
ar << m_iIdSeason;
+ ar << m_iPlayTitle;
@@ -417,6 +421,7 @@ void CVideoInfoTag::Archive(CArchive& ar)
ar >> m_type;
ar >> m_iIdSeason;
+ ar >> m_iPlayTitle;
@@ -484,6 +489,7 @@ void CVideoInfoTag::Serialize(CVariant& value) const
value["dateadded"] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::EmptyString;
value["type"] = m_type;
value["seasonid"] = m_iIdSeason;
+ value["playtitle"] = m_iPlayTitle;
void CVideoInfoTag::ToSortable(SortItem& sortable)
@@ -523,6 +529,7 @@ void CVideoInfoTag::ToSortable(SortItem& sortable)
sortable[FieldId] = m_iDbId;
sortable[FieldTrackNumber] = m_iTrack;
sortable[FieldTag] = m_tags;
+ sortable[FieldPlayTitle] = m_iPlayTitle;
if (m_streamDetails.HasItems() && m_streamDetails.GetVideoDuration() > 0)
sortable[FieldTime] = m_streamDetails.GetVideoDuration();
@@ -564,6 +571,9 @@ void CVideoInfoTag::ParseNative(const TiXmlElement* movie, bool prioritise)
XMLUtils::GetString(movie, "sorttitle", m_strSortTitle);
XMLUtils::GetFloat(movie, "rating", m_fRating);
XMLUtils::GetFloat(movie, "epbookmark", m_fEpBookmark);
+ CStdString temp;
+ if( !XMLUtils::GetInt(movie, "playtitle", m_iPlayTitle) )
+ m_iPlayTitle = 100000;
int max_value = 10;
const TiXmlElement* rElement = movie->FirstChildElement("rating");
if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
2 xbmc/video/VideoInfoTag.h
@@ -128,6 +128,8 @@ class CVideoInfoTag : public IArchivable, public ISerializable, public ISortable
int m_iTrack;
float m_fRating;
float m_fEpBookmark;
+ int m_iPlayTitle; // title to play for optical media 100000 play menu,
+ //0 play longest, n only play title n (playlist nnnnn.mpls) then stop, -n play title n-1
int m_iBookmarkId;
int m_iIdShow;
int m_iIdSeason;
2 xbmc/video/windows/GUIWindowVideoBase.cpp
@@ -1047,7 +1047,7 @@ bool CGUIWindowVideoBase::ShowPlaySelection(CFileItemPtr& item)
if (item->m_lStartOffset)
return true;
- if (item->IsBDFile())
+ if (item->IsBDFile() && !(item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iPlayTitle != 100000))
CStdString root = URIUtils::GetParentPath(item->GetPath());
Something went wrong with that request. Please try again.