>From f0904393b1c209e0bec62706ab6c6c8b781ad023 Mon Sep 17 00:00:00 2001
From: Antoine Lejeune <phytos@videolan.org>
Date: Tue, 29 Apr 2008 16:15:28 +0200
Subject: [PATCH] Ability to remember the item playing when VLC is closed so that when VLC
 is restarted the item can restart at its last position.

---
 include/vlc_input.h                  |    5 +-
 include/vlc_playlist.h               |    3 +
 modules/gui/macosx/intf.m            |    2 +-
 modules/gui/qt4/qt4.cpp              |    2 +-
 modules/gui/skins2/src/skin_main.cpp |    2 +-
 modules/gui/wxwidgets/wxwidgets.cpp  |    2 +-
 src/input/control.c                  |   23 ++++++
 src/libvlc-common.c                  |    2 +-
 src/libvlc-module.c                  |   13 ++++
 src/playlist/control.c               |   21 ++++++
 src/playlist/engine.c                |    2 +
 src/playlist/loadsave.c              |  129 ++++++++++++++++++++++++++++++++--
 src/playlist/playlist_internal.h     |    4 +
 13 files changed, 199 insertions(+), 11 deletions(-)

diff --git a/include/vlc_input.h b/include/vlc_input.h
index 738b374..cc35e4d 100644
--- a/include/vlc_input.h
+++ b/include/vlc_input.h
@@ -654,7 +654,10 @@ enum input_query_e
     INPUT_GET_ATTACHMENT,  /* arg1=input_attachment_t**, arg2=char*  res=can fail */
 
     /* On the fly input slave */
-    INPUT_ADD_SLAVE        /* arg1= char * */
+    INPUT_ADD_SLAVE,       /* arg1= char * */
+
+    /* Saving to the start-time option the playing time */
+    INPUT_SAVE_TIME        /* res= */
 };
 
 VLC_EXPORT( int, input_vaControl,( input_thread_t *, int i_query, va_list  ) );
diff --git a/include/vlc_playlist.h b/include/vlc_playlist.h
index baa2217..1648728 100644
--- a/include/vlc_playlist.h
+++ b/include/vlc_playlist.h
@@ -303,6 +303,9 @@ VLC_EXPORT( int, playlist_PreparseEnqueueItem, (playlist_t *, playlist_item_t *)
 /** Request the art for an input item to be fetched */
 VLC_EXPORT( int, playlist_AskForArtEnqueue, (playlist_t *, input_item_t *) );
 
+/** Start the playlist when the interface is created */
+VLC_EXPORT( int, playlist_PlayOnStart, (playlist_t *) );
+
 /********************** Services discovery ***********************/
 
 /** Add a list of comma-separated service discovery modules */
diff --git a/modules/gui/macosx/intf.m b/modules/gui/macosx/intf.m
index bdea568..38662da 100644
--- a/modules/gui/macosx/intf.m
+++ b/modules/gui/macosx/intf.m
@@ -560,7 +560,7 @@ static VLCMain *_o_sharedMainInstance = nil;
     /* Check if we need to start playing */
     if( p_intf->b_play )
     {
-        playlist_Control( p_playlist, PLAYLIST_PLAY, false );
+        playlist_PlayOnStart( p_playlist );
     }
     var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
     val.b_bool = false;
diff --git a/modules/gui/qt4/qt4.cpp b/modules/gui/qt4/qt4.cpp
index f22fc7b..8bfc809 100644
--- a/modules/gui/qt4/qt4.cpp
+++ b/modules/gui/qt4/qt4.cpp
@@ -355,7 +355,7 @@ static void Init( intf_thread_t *p_intf )
     /* Start playing if needed */
     if( !p_intf->pf_show_dialog && p_intf->b_play )
     {
-        playlist_Control( THEPL, PLAYLIST_PLAY, false );
+        playlist_PlayOnStart( THEPL );
     }
 
     /* Explain to the core how to show a dialog :D */
diff --git a/modules/gui/skins2/src/skin_main.cpp b/modules/gui/skins2/src/skin_main.cpp
index aaff854..ff9dd5b 100644
--- a/modules/gui/skins2/src/skin_main.cpp
+++ b/modules/gui/skins2/src/skin_main.cpp
@@ -259,7 +259,7 @@ static void Run( intf_thread_t *p_intf )
                                            FIND_ANYWHERE );
         if( p_playlist )
         {
-            playlist_Control( p_playlist, PLAYLIST_PLAY, false );
+            playlist_PlayOnStart( p_playlist );
             vlc_object_release( p_playlist );
         }
     }
diff --git a/modules/gui/wxwidgets/wxwidgets.cpp b/modules/gui/wxwidgets/wxwidgets.cpp
index ffeb2d3..85cb242 100644
--- a/modules/gui/wxwidgets/wxwidgets.cpp
+++ b/modules/gui/wxwidgets/wxwidgets.cpp
@@ -401,7 +401,7 @@ bool Instance::OnInit()
                                            FIND_ANYWHERE );
         if( p_playlist )
         {
-            playlist_Control( p_playlist, PLAYLIST_PLAY, false );
+            playlist_PlayOnStart( p_playlist );
             vlc_object_release( p_playlist );
         }
     }
diff --git a/src/input/control.c b/src/input/control.c
index 0609c60..74f0da9 100644
--- a/src/input/control.c
+++ b/src/input/control.c
@@ -589,6 +589,29 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args )
             return VLC_EGENERIC;
         }
 
+        case INPUT_SAVE_TIME:
+        {
+            char *psz_time;
+            if( asprintf( &psz_time, I64Fd, p_input->i_time/1000000 ) == -1 )
+                psz_time = NULL;
+            input_Control( p_input, INPUT_ADD_OPTION, "start-time", psz_time );
+
+            // If it's a dvd://, rename it if its title is > 0 (not the menu)
+            const char *psz_access, *psz_demux;
+            char *psz_path;
+            char *psz_uri = strdup( p_input->p->input.p_item->psz_uri );
+
+            MRLSplit( psz_uri, &psz_access, &psz_demux, &psz_path );
+            if( strcmp( psz_access, "dvd" ) == 0 && 
+                var_GetInteger( p_input, "title" ) > 0 &&
+                asprintf( &p_input->p->input.p_item->psz_uri,
+                          "dvdsimple://%s", psz_path ) == -1 )
+                p_input->p->input.p_item->psz_uri = NULL;
+
+            free( psz_uri );
+
+            return VLC_SUCCESS;
+        }
 
         default:
             msg_Err( p_input, "unknown query in input_vaControl" );
diff --git a/src/libvlc-common.c b/src/libvlc-common.c
index ee4dfe7..7f1811f 100644
--- a/src/libvlc-common.c
+++ b/src/libvlc-common.c
@@ -1152,7 +1152,7 @@ int libvlc_InternalAddIntf( libvlc_int_t *p_libvlc,
 
     /* Interface doesn't handle play on start so do it ourselves */
     if( !p_intf->b_play && b_play )
-        playlist_Play( p_libvlc->p_playlist );
+        playlist_PlayOnStart( p_libvlc->p_playlist );
 
     /* Try to run the interface */
     p_intf->b_play = b_play;
diff --git a/src/libvlc-module.c b/src/libvlc-module.c
index 23ec831..6ee956a 100644
--- a/src/libvlc-module.c
+++ b/src/libvlc-module.c
@@ -1116,6 +1116,16 @@ static const char *ppsz_albumart_descriptions[] =
     "The media library is automatically saved and reloaded each time you " \
     "start VLC." )
 
+#define RPL_TEXT N_("Remember the playing item")
+#define RPL_LONGTEXT N_( \
+    "Remember the item playing when VLC was closed so that it will be " \
+    "reopened at the correct position." )
+static int pi_rpl_values[] = { 0, 1, 2 };
+static const char *ppsz_rpl_descriptions[] =
+    { N_("Do not remember"),
+      N_("Add it to the playlist"),
+      N_("Play it") };
+
 #define PLTREE_TEXT N_("Display playlist tree")
 #define PLTREE_LONGTEXT N_( \
     "The playlist can use a tree to categorize some items, like the " \
@@ -1612,6 +1622,7 @@ vlc_module_begin();
                  INPUT_REPEAT_TEXT, INPUT_REPEAT_LONGTEXT, false );
     add_integer( "start-time", 0, NULL,
                  START_TIME_TEXT, START_TIME_LONGTEXT, true );
+    change_safe();
     add_integer( "stop-time", 0, NULL,
                  STOP_TIME_TEXT, STOP_TIME_LONGTEXT, true );
     add_integer( "run-time", 0, NULL,
@@ -1871,6 +1882,8 @@ vlc_module_begin();
                  ALBUM_ART_LONGTEXT, false );
         change_integer_list( pi_albumart_values,
                              ppsz_albumart_descriptions, 0 );
+    add_integer( "remember-playing-item", true, NULL, RPL_TEXT, RPL_LONGTEXT, false );
+        change_integer_list( pi_rpl_values, ppsz_rpl_descriptions, 0 );
 
     set_subcategory( SUBCAT_PLAYLIST_SD );
     add_module_list_cat( "services-discovery", SUBCAT_PLAYLIST_SD, NULL,
diff --git a/src/playlist/control.c b/src/playlist/control.c
index d07f5d1..b2c223f 100644
--- a/src/playlist/control.c
+++ b/src/playlist/control.c
@@ -168,6 +168,27 @@ static int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args
 }
 
 /*****************************************************************************
+ * Start playing when the interface is created
+ *****************************************************************************/
+int playlist_PlayOnStart( playlist_t *p_playlist )
+{
+    int i_remember = config_GetInt( p_playlist, "remember-playing-item");
+
+    // First, we start the playlist to play the command line argument
+    playlist_Control( p_playlist, PLAYLIST_PLAY, false );
+
+    // We remember the last playing item
+    if( i_remember != 0 )
+        playlist_RestorePlayingItem( p_playlist );
+
+    // We want to play it
+    if( i_remember != 1 )
+        playlist_Control( p_playlist, PLAYLIST_PLAY, false );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
  * Preparse control
  *****************************************************************************/
 /** Enqueue an item for preparsing */
diff --git a/src/playlist/engine.c b/src/playlist/engine.c
index 4359ee5..229b688 100644
--- a/src/playlist/engine.c
+++ b/src/playlist/engine.c
@@ -434,6 +434,7 @@ void playlist_LastLoop( playlist_t *p_playlist )
         }
         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
         {
+            input_Control( p_playlist->p_input, INPUT_SAVE_TIME);
             input_StopThread( p_playlist->p_input );
             PL_UNLOCK;
             continue;
@@ -471,6 +472,7 @@ void playlist_LastLoop( playlist_t *p_playlist )
                                           p_playlist->pp_sds[0]->p_sd->psz_module );
     }
 
+    playlist_SavePlayingItem( p_playlist );
     playlist_MLDump( p_playlist );
 
     PL_LOCK;
diff --git a/src/playlist/loadsave.c b/src/playlist/loadsave.c
index 1579db3..0dc9afc 100644
--- a/src/playlist/loadsave.c
+++ b/src/playlist/loadsave.c
@@ -113,11 +113,7 @@ int playlist_MLLoad( playlist_t *p_playlist )
     input_item_t *p_input;
 
     if( !config_GetInt( p_playlist, "media-library") ) return VLC_SUCCESS;
-    if( !psz_datadir ) /* XXX: This should never happen */
-    {
-        msg_Err( p_playlist, "no data directory, cannot load media library") ;
-        return VLC_EGENERIC;
-    }
+    assert( psz_datadir );
 
     if( asprintf( &psz_uri, "%s" DIR_SEP "ml.xspf", psz_datadir ) == -1 )
     {
@@ -213,3 +209,126 @@ int playlist_MLDump( playlist_t *p_playlist )
 
     return VLC_SUCCESS;
 }
+
+/*****************************************************************************
+ * A subitem has been added to the Playlist (Event Callback)
+ *****************************************************************************/
+static void input_item_subitem_added_to_PL( const vlc_event_t * p_event,
+                                      void * user_data )
+{
+    playlist_t *p_playlist = user_data;
+    input_item_t *p_item = p_event->u.input_item_subitem_added.p_new_child;
+
+    /* playlist_AddInput() can fail, but we have no way to report that ..
+     * Any way when it has failed, either the playlist is dying, either OOM */
+    playlist_AddInput( p_playlist, p_item, PLAYLIST_APPEND, PLAYLIST_END,
+            true, false );
+}
+
+int playlist_RestorePlayingItem( playlist_t *p_playlist )
+{
+    const char *psz_datadir = p_playlist->p_libvlc->psz_datadir;
+    char *psz_uri = NULL;
+    input_item_t *p_input;
+
+    if( !config_GetInt( p_playlist, "remember-playing-item") ) 
+        return VLC_SUCCESS;
+    assert( psz_datadir );
+
+    if( asprintf( &psz_uri, "%s" DIR_SEP "last-played.xspf", psz_datadir ) == -1 )
+    {
+        psz_uri = NULL;
+        goto error;
+    }
+    struct stat p_stat;
+    /* checks if playing item file is present */
+    if( utf8_stat( psz_uri , &p_stat ) )
+    {
+        free( psz_uri );
+        return VLC_EGENERIC;
+    }
+    free( psz_uri );
+
+    if( asprintf( &psz_uri, "file/xspf-open://%s" DIR_SEP "last-played.xspf",
+                  psz_datadir ) == -1 )
+    {
+        psz_uri = NULL;
+        goto error;
+    }
+
+    p_input = input_ItemNewExt( p_playlist, psz_uri, 
+            "Playing when VLC was closed",
+            0, NULL, -1 );
+
+    vlc_event_attach( &p_input->event_manager, vlc_InputItemSubItemAdded,
+                            input_item_subitem_added_to_PL, p_playlist );
+    input_Read( p_playlist, p_input, true );
+    vlc_event_detach( &p_input->event_manager, vlc_InputItemSubItemAdded,
+                            input_item_subitem_added_to_PL, p_playlist );
+
+    vlc_gc_decref( p_input );
+    free( psz_uri );
+    return VLC_SUCCESS;
+
+error:
+    free( psz_uri );
+    return VLC_ENOMEM;
+}
+
+int playlist_SavePlayingItem( playlist_t *p_playlist )
+{
+    char *psz_datadir = p_playlist->p_libvlc->psz_datadir;
+    if( !config_GetInt( p_playlist, "remember-playing-item" ) )
+        return VLC_SUCCESS;
+    if( !psz_datadir ) /* XXX: This should never happen */
+    {
+        msg_Err( p_playlist, "no data directory, cannot save the playing item" );
+        return VLC_EGENERIC;
+    }
+
+    char psz_dirname[ strlen( psz_datadir )
+                      + sizeof( DIR_SEP "last-played.xspf")];
+    sprintf( psz_dirname, "%s", psz_datadir );
+    if( config_CreateDir( (vlc_object_t *)p_playlist, psz_dirname ) )
+    {
+        return VLC_EGENERIC;
+    }
+
+    strcat( psz_dirname, DIR_SEP "last-played.xspf" );
+
+    // If playlist is stopped then we don't want to save anything and delete 
+    // last-played.xspf if it exists.
+    if( p_playlist->status.i_status == PLAYLIST_STOPPED )
+    {
+        utf8_unlink( psz_dirname );
+        return VLC_SUCCESS;
+    }
+
+    /* In order to save the playing item, it must be the only child of a node.
+       The playlist is dead, we can't use playlist_NodeCreate so we create a
+       new node from the playing item and we add the playing item to this new
+       node. */
+
+    // We allocate the memory for the node
+    DECMALLOC_ERR( p_item, playlist_item_t );
+    playlist_item_t *p_current = ARRAY_VAL( p_playlist->current,
+            p_playlist->i_current_index );
+    // We set the properties of the node
+    p_item->p_input = p_current->p_input;
+    p_item->i_id = ++p_playlist->i_last_playlist_id;
+    p_item->p_parent = NULL;
+    p_item->i_children = 0;
+    p_item->pp_children = NULL;
+    p_item->i_flags = 0;
+    p_item->p_playlist = p_playlist;
+    p_item->i_children = 0;
+
+    // We add the playing item to the node
+    playlist_NodeAppend( p_playlist, p_current, p_item );
+
+    playlist_Export( p_playlist, psz_dirname, p_item, "export-xspf" );
+
+    free( p_item );
+
+    return VLC_SUCCESS;
+}
diff --git a/src/playlist/playlist_internal.h b/src/playlist/playlist_internal.h
index 94ab3f6..4829464 100644
--- a/src/playlist/playlist_internal.h
+++ b/src/playlist/playlist_internal.h
@@ -84,6 +84,10 @@ int playlist_PlayItem  ( playlist_t *, playlist_item_t * );
 int playlist_MLLoad( playlist_t *p_playlist );
 int playlist_MLDump( playlist_t *p_playlist );
 
+/* Load/Save of last playing item */
+int playlist_SavePlayingItem( playlist_t *p_playlist );
+int playlist_RestorePlayingItem( playlist_t *p_playlist );
+
 /**********************************************************************
  * Item management
  **********************************************************************/
-- 
1.5.5.1

