[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.

Antoine Lejeune phytos at videolan.org
Tue Apr 29 16:15:28 CEST 2008


---
 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


--7JfCtLOvnd9MIVvH--


More information about the vlc-devel mailing list