Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
b3d51b45 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Publicly expose getMediaLibrary

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
d7199341 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Add VLCPlayQueueController methods to create a playlist from current 
playqueue items

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
e2a3d9d8 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Add menu item to create a playlist from playqueue

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
5135c687 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Indicate whether media library items are file-backed or not

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
c303f8d7 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Differentiate between file and non-file backed playlists, perform 
different trashing procedures

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
4a374d1b by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Update "move to trash" button depending on file-backed state

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
8a088770 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Specify array types in playqueue model

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
45558b1d by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Expose playqueue model's items in readonly array property

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
c7609f22 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Move playlist creation handling to library controller class

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -
a64451e0 by Claudio Cambra at 2025-08-28T11:41:46+00:00
macosx: Add main menu entry to create playlist from playqueue

Signed-off-by: Claudio Cambra <develo...@claudiocambra.com>

- - - - -


12 changed files:

- modules/gui/macosx/UI/MainMenu.xib
- modules/gui/macosx/library/VLCLibraryController.h
- modules/gui/macosx/library/VLCLibraryController.m
- modules/gui/macosx/library/VLCLibraryDataTypes.h
- modules/gui/macosx/library/VLCLibraryDataTypes.m
- modules/gui/macosx/library/VLCLibraryMenuController.m
- modules/gui/macosx/menus/VLCMainMenu.h
- modules/gui/macosx/menus/VLCMainMenu.m
- modules/gui/macosx/playqueue/VLCPlayQueueController.m
- modules/gui/macosx/playqueue/VLCPlayQueueMenuController.m
- modules/gui/macosx/playqueue/VLCPlayQueueModel.h
- modules/gui/macosx/playqueue/VLCPlayQueueModel.m


Changes:

=====================================
modules/gui/macosx/UI/MainMenu.xib
=====================================
@@ -111,6 +111,7 @@
                 <outlet property="rendererNoneItem" destination="eE8-qf-9x9" 
id="Ol2-yz-0eF"/>
                 <outlet property="repeat" destination="5143" id="ulG-wD-gtG"/>
                 <outlet property="revealInFinder" destination="3945" 
id="z7n-A2-K5o"/>
+                <outlet property="savePlayqueueToLibrary" 
destination="24V-9x-Aqt" id="fic-89-xZr"/>
                 <outlet property="save_playlist" destination="1599" 
id="spC-pk-6W4"/>
                 <outlet property="screen" destination="1016" id="VFv-ey-gJR"/>
                 <outlet property="screenMenu" destination="1015" 
id="l5t-0E-DfG"/>
@@ -335,6 +336,12 @@
                                     <action selector="savePlaylist:" 
target="-2" id="iHV-3W-w6Z"/>
                                 </connections>
                             </menuItem>
+                            <menuItem title="Save Play Queue to Library..." 
keyEquivalent="s" id="24V-9x-Aqt">
+                                <modifierMask key="keyEquivalentModifierMask" 
option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="savePlayQueueToLibrary:" 
target="-2" id="UzJ-9r-vul"/>
+                                </connections>
+                            </menuItem>
                         </items>
                     </menu>
                 </menuItem>


=====================================
modules/gui/macosx/library/VLCLibraryController.h
=====================================
@@ -27,6 +27,7 @@
 @class VLCInputItem;
 @class VLCLibraryModel;
 @class VLCMediaLibraryMediaItem;
+@class VLCPlayQueueItem;
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -48,6 +49,9 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (int)clearHistory;
 
+- (void)showCreatePlaylistDialogForPlayQueue;
+- (void)showCreatePlaylistDialogForPlayQueueItems:(NSArray<VLCPlayQueueItem *> 
*)items;
+
 /**
  * Sort the entire library representation based on:
  * @param sortCriteria the criteria used for sorting


=====================================
modules/gui/macosx/library/VLCLibraryController.m
=====================================
@@ -22,6 +22,8 @@
 
 #import "VLCLibraryController.h"
 
+#import "extensions/NSString+Helpers.h"
+
 #import "main/VLCMain.h"
 
 #import "playqueue/VLCPlayQueueController.h"
@@ -32,6 +34,9 @@
 #import "library/VLCLibraryDataTypes.h"
 #import "library/VLCMediaLibraryFolderObserver.h"
 
+#import "playqueue/VLCPlayQueueItem.h"
+#import "playqueue/VLCPlayQueueModel.h"
+
 #import <vlc_media_library.h>
 
 typedef int (*folder_action_f)(vlc_medialibrary_t*, const char*);
@@ -213,6 +218,87 @@ typedef int (*folder_action_f)(vlc_medialibrary_t*, const 
char*);
     return vlc_ml_clear_history(_p_libraryInstance, 
VLC_ML_HISTORY_TYPE_GLOBAL);
 }
 
+- (void)showCreatePlaylistDialogForPlayQueue
+{
+    VLCPlayQueueController * const playQueueController = 
VLCMain.sharedInstance.playQueueController;
+    NSArray<VLCPlayQueueItem *> * const items = 
playQueueController.playQueueModel.playQueueItems;
+    [self showCreatePlaylistDialogForPlayQueueItems:items];
+}
+
+- (void)showCreatePlaylistDialogForPlayQueueItems:(NSArray<VLCPlayQueueItem *> 
*)items
+{
+    if (!items || items.count == 0) {
+        return;
+    }
+
+    NSAlert * const alert = [[NSAlert alloc] init];
+    alert.messageText = _NS("Create New Playlist");
+    alert.informativeText = _NS("Enter a name for the new playlist:");
+    [alert addButtonWithTitle:_NS("Create")];
+    [alert addButtonWithTitle:_NS("Cancel")];
+
+    NSTextField * const input = [[NSTextField alloc] 
initWithFrame:NSMakeRect(0, 0, 300, 24)];
+    input.placeholderString = _NS("Playlist Name");
+    alert.accessoryView = input;
+
+    alert.window.initialFirstResponder = input;
+
+    const NSModalResponse response = [alert runModal];
+    if (response != NSAlertFirstButtonReturn) {
+        return;
+    }
+
+    NSString * const playlistName = input.stringValue;
+    if (playlistName.length == 0) {
+        return;
+    }
+
+    const BOOL success = [self createPlaylistWithName:playlistName 
fromItems:items];
+    if (success) {
+        return;
+    }
+
+    NSAlert * const errorAlert = [[NSAlert alloc] init];
+    errorAlert.messageText = _NS("Failed to Create Playlist");
+    errorAlert.informativeText = _NS("The playlist could not be created. 
Please try again.");
+    errorAlert.alertStyle = NSAlertStyleWarning;
+    [errorAlert addButtonWithTitle:_NS("OK")];
+    [errorAlert runModal];
+}
+
+- (BOOL)createPlaylistWithName:(NSString *)playlistName 
fromItems:(NSArray<VLCPlayQueueItem *> *)items
+{
+    if (!_p_libraryInstance || !playlistName || playlistName.length == 0) {
+        return NO;
+    }
+    
+    vlc_ml_playlist_t * const playlist = 
vlc_ml_playlist_create(_p_libraryInstance, playlistName.UTF8String);
+    if (!playlist) {
+        msg_Err(getIntf(), "Failed to create playlist with name: %s", 
playlistName.UTF8String);
+        return NO;
+    }
+    
+    const int64_t playlistId = playlist->i_id;
+    vlc_ml_playlist_release(playlist);
+    
+    for (VLCPlayQueueItem * const item in items) {
+        VLCMediaLibraryMediaItem * const mediaLibraryItem = 
item.mediaLibraryItem;
+        
+        if (!mediaLibraryItem) {
+            msg_Warn(getIntf(), "No media library item found for playqueue 
item with name '%s'", item.title.UTF8String);
+            continue;
+        }
+        
+        const int64_t mediaId = mediaLibraryItem.libraryID;
+        const int result = vlc_ml_playlist_append(_p_libraryInstance, 
playlistId, &mediaId, 1);
+        if (result != VLC_SUCCESS) {
+            msg_Warn(getIntf(), "Failed to add media library item %lld to 
playlist", mediaLibraryItem.libraryID);
+        }
+    }
+    
+    return YES;
+}
+
 - (void)sortByCriteria:(enum vlc_ml_sorting_criteria_t)sortCriteria 
andDescending:(bool)descending
 {
     _unsorted = NO;


=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.h
=====================================
@@ -40,6 +40,8 @@ extern const CGFloat VLCMediaLibrary720pWidth;
 extern const CGFloat VLCMediaLibrary720pHeight;
 extern const long long int VLCMediaLibraryMediaItemDurationDenominator;
 
+vlc_medialibrary_t * _Nullable getMediaLibrary(void);
+
 typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
     VLCMediaLibraryParentGroupTypeUnknown = VLC_ML_PARENT_UNKNOWN,
     VLCMediaLibraryParentGroupTypeAlbum = VLC_ML_PARENT_ALBUM,
@@ -122,6 +124,7 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) 
{
 @property (readonly) BOOL smallArtworkGenerated;
 @property (readonly) NSString *smallArtworkMRL;
 @property (readonly) NSString *displayString;
+@property (readonly) BOOL isFileBacked;
 @property (readonly) NSString *primaryDetailString;
 @property (readonly) NSString *secondaryDetailString;
 @property (readonly) NSString *durationString;


=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.m
=====================================
@@ -46,9 +46,9 @@ typedef vlc_ml_album_list_t* 
(*library_album_list_fetch_f)(vlc_medialibrary_t*,
 typedef vlc_ml_artist_list_t* 
(*library_artist_list_fetch_f)(vlc_medialibrary_t*, const 
vlc_ml_query_params_t*, int64_t);
 typedef int (*library_item_set_favorite_f)(vlc_medialibrary_t*, int64_t, bool);
 
-static vlc_medialibrary_t *getMediaLibrary(void)
+vlc_medialibrary_t * _Nullable getMediaLibrary(void)
 {
-    intf_thread_t *p_intf = getIntf();
+    intf_thread_t * const p_intf = getIntf();
     if (!p_intf) {
         return nil;
     }
@@ -319,6 +319,11 @@ static NSString 
*genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     return nil;
 }
 
+- (BOOL)isFileBacked
+{
+    return YES;
+}
+
 - (id<VLCMediaLibraryItemProtocol>)primaryActionableDetailLibraryItem
 {
     [self doesNotRecognizeSelector:_cmd];
@@ -996,13 +1001,63 @@ static NSString 
*genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
     return self.mediaItems.firstObject;
 }
 
-- (void)moveToTrash
+- (BOOL)isFileBacked
 {
-    NSFileManager * const fileManager = NSFileManager.defaultManager;
+    if (_MRL == nil || _MRL.length == 0) {
+        return NO;
+    }
+
     NSURL * const URL = [NSURL URLWithString:_MRL];
-    [fileManager trashItemAtURL:URL
-               resultingItemURL:nil
-                          error:nil];
+    if (URL == nil || !URL.isFileURL) {
+        return NO;
+    }
+
+    return [NSFileManager.defaultManager fileExistsAtPath:URL.path];
+}
+
+- (void)moveToTrash
+{
+    // First check if this playlist has a valid file MRL (e.g., .m3u file)
+    if (_MRL != nil && _MRL.length > 0) {
+        NSURL * const URL = [NSURL URLWithString:_MRL];
+        if (URL == nil || !URL.isFileURL) {
+            NSLog(@"Playlist %@ is not file-backed or is a dir (?)", 
self.displayString);
+            return;
+        }
+        NSFileManager * const fileManager = NSFileManager.defaultManager;
+        const BOOL fileExists = [fileManager fileExistsAtPath:URL.path];
+        
+        if (!fileExists) {
+            NSLog(@"Playlist file does not exist: %@", URL.path);
+            return;
+        }
+        
+        // This is a file-based playlist, move the file to trash
+        NSError *error = nil;
+        [fileManager trashItemAtURL:URL
+                    resultingItemURL:nil
+                                error:&error];
+        if (error) {
+            NSLog(@"Failed to move playlist file to trash: %@", error);
+        } else {
+            NSLog(@"Successfully moved playlist file %@ to trash", 
URL.lastPathComponent);
+            return;
+        }
+    }
+    
+    // If no valid file MRL or file doesn't exist, delete from media library
+    vlc_medialibrary_t * const p_ml = getMediaLibrary();
+    if (p_ml == NULL) {
+        NSLog(@"Could not get media library to delete playlist %@", 
self.displayString);
+        return;
+    }
+    
+    const int result = vlc_ml_playlist_delete(p_ml, self.libraryID);
+    if (result != VLC_SUCCESS) {
+        NSLog(@"Failed to delete playlist %@ with ID %lld from media library", 
self.displayString, self.libraryID);
+    } else {
+        NSLog(@"Successfully deleted playlist %@ with ID %lld from media 
library", self.displayString, self.libraryID);
+    }
 }
 
 - (void)revealInFinder
@@ -1053,6 +1108,7 @@ static NSString 
*genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
 @synthesize primaryActionableDetailLibraryItem = 
_primaryActionableDetailLibraryItem;
 @synthesize secondaryActionableDetailLibraryItem = 
_secondaryActionableDetailLibraryItem;
 @synthesize favorited = _favorited;
+@synthesize isFileBacked = _isFileBacked;
 
 #pragma mark - initialization
 
@@ -1150,6 +1206,7 @@ static NSString 
*genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
         _progress = p_mediaItem->f_progress;
         _favorited = p_mediaItem->b_is_favorite;
         _title = toNSStr(p_mediaItem->psz_title);
+        _isFileBacked = YES;
 
         switch (p_mediaItem->i_subtype) {
             case VLC_ML_MEDIA_SUBTYPE_MOVIE:
@@ -1801,6 +1858,7 @@ static NSString 
*genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
 @synthesize secondaryActionableDetailLibraryItem = 
_secondaryActionableDetailLibraryItem;
 @synthesize labels = _labels;
 @synthesize favorited = _favorited;
+@synthesize isFileBacked = _isFileBacked;
 
 - (instancetype)initWithDisplayString:(NSString *)displayString
               withPrimaryDetailString:(nullable NSString *)primaryDetailString
@@ -1815,6 +1873,7 @@ static NSString 
*genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
         _libraryId = -1;
         _smallArtworkGenerated = NO;
         _smallArtworkMRL = @"";
+        _isFileBacked = NO;
         _primaryActionableDetail = NO;
         _primaryActionableDetailLibraryItem = nil;
         _secondaryActionableDetail = NO;


=====================================
modules/gui/macosx/library/VLCLibraryMenuController.m
=====================================
@@ -49,6 +49,8 @@
     NSHashTable<NSMenuItem*> *_inputItemRequiringMenuItems;
     NSHashTable<NSMenuItem*> *_localInputItemRequiringMenuItems;
     NSHashTable<NSMenuItem*> *_folderInputItemRequiringMenuItems;
+    
+    NSMenuItem *_deleteItem;
 }
 
 @property (readwrite) NSMenuItem *favoriteItem;
@@ -80,8 +82,8 @@
     NSMenuItem *revealItem = [[NSMenuItem alloc] initWithTitle:_NS("Reveal in 
Finder") action:@selector(revealInFinder:) keyEquivalent:@""];
     revealItem.target = self;
 
-    NSMenuItem *deleteItem = [[NSMenuItem alloc] initWithTitle:_NS("Move to 
Trash") action:@selector(moveToTrash:) keyEquivalent:@""];
-    deleteItem.target = self;
+    _deleteItem = [[NSMenuItem alloc] initWithTitle:_NS("Move to Trash") 
action:@selector(moveToTrash:) keyEquivalent:@""];
+    _deleteItem.target = self;
 
     NSMenuItem *markUnseenItem = [[NSMenuItem alloc] initWithTitle:_NS("Mark 
as Unseen") action:@selector(markUnseen:) keyEquivalent:@""];
     markUnseenItem.target = self;
@@ -104,7 +106,7 @@
         self.favoriteItem,
         bookmarkItem,
         revealItem,
-        deleteItem,
+        _deleteItem,
         markUnseenItem,
         informationItem,
         [NSMenuItem separatorItem], 
@@ -116,7 +118,7 @@
     [_mediaItemRequiringMenuItems addObject:appendItem];
     [_mediaItemRequiringMenuItems addObject:self.favoriteItem];
     [_mediaItemRequiringMenuItems addObject:revealItem];
-    [_mediaItemRequiringMenuItems addObject:deleteItem];
+    [_mediaItemRequiringMenuItems addObject:_deleteItem];
     [_mediaItemRequiringMenuItems addObject:informationItem];
 
     _recentsMediaItemRequiringMenuItems = [NSHashTable weakObjectsHashTable];
@@ -128,7 +130,7 @@
 
     _localInputItemRequiringMenuItems = [NSHashTable weakObjectsHashTable];
     [_localInputItemRequiringMenuItems addObject:revealItem];
-    [_localInputItemRequiringMenuItems addObject:deleteItem];
+    [_localInputItemRequiringMenuItems addObject:_deleteItem];
 
     _folderInputItemRequiringMenuItems = [NSHashTable weakObjectsHashTable];
     [_folderInputItemRequiringMenuItems addObject:bookmarkItem];
@@ -173,6 +175,29 @@
         }
         self.favoriteItem.title = anyUnfavorited ? _NS("Add to Favorites") : 
_NS("Remove from Favorites");
         self.favoriteItem.action = anyUnfavorited ? @selector(addFavorite:) : 
@selector(removeFavorite:);
+        
+        // Update delete menu item title based on whether items are file-backed
+        BOOL hasFileBacked = NO;
+        BOOL hasNonFileBacked = NO;
+        
+        for (VLCLibraryRepresentedItem * const item in self.representedItems) {
+            if (item.item.isFileBacked) {
+                hasFileBacked = YES;
+            } else {
+                hasNonFileBacked = YES;
+            }
+            if (hasFileBacked && hasNonFileBacked) {
+                break;
+            }
+        }
+        
+        if (hasFileBacked && hasNonFileBacked) {
+            _deleteItem.title = _NS("Move to Trash / Delete from Library");
+        } else if (hasFileBacked) {
+            _deleteItem.title = _NS("Move to Trash");
+        } else {
+            _deleteItem.title = _NS("Delete from Library");
+        }
 
     } else if (_representedInputItems != nil && 
self.representedInputItems.count > 0) {
         [self menuItems:_mediaItemRequiringMenuItems setHidden:YES];


=====================================
modules/gui/macosx/menus/VLCMainMenu.h
=====================================
@@ -54,6 +54,7 @@
 @property (readwrite, weak) IBOutlet NSMenuItem *close_window;
 @property (readwrite, weak) IBOutlet NSMenuItem *convertandsave;
 @property (readwrite, weak) IBOutlet NSMenuItem *save_playlist;
+@property (readwrite, weak) IBOutlet NSMenuItem *savePlayqueueToLibrary;
 @property (readwrite, weak) IBOutlet NSMenuItem *revealInFinder;
 
 @property (readwrite, weak) IBOutlet NSMenu *editMenu;
@@ -237,6 +238,7 @@
 - (IBAction)intfOpenNet:(id)sender;
 - (IBAction)intfOpenCapture:(id)sender;
 - (IBAction)savePlaylist:(id)sender;
+- (IBAction)savePlayQueueToLibrary:(id)sender;
 
 - (IBAction)play:(id)sender;
 - (IBAction)stop:(id)sender;


=====================================
modules/gui/macosx/menus/VLCMainMenu.m
=====================================
@@ -27,6 +27,7 @@
 #import "extensions/NSScreen+VLCAdditions.h"
 #import "extensions/NSString+Helpers.h"
 
+#import "library/VLCLibraryController.h"
 #import "library/VLCLibraryWindow.h"
 #import "library/VLCLibraryWindowController.h"
 #import "library/VLCLibraryWindowSplitViewController.h"
@@ -360,6 +361,7 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
     [_close_window setTitle: _NS("Close Window")];
     [_convertandsave setTitle: _NS("Convert / Stream...")];
     [_save_playlist setTitle: _NS("Save Playlist...")];
+    [_savePlayqueueToLibrary setTitle: _NS("Save Play Queue to Library...")];
     [_revealInFinder setTitle: _NS("Reveal in Finder")];
 
     [_editMenu setTitle: _NS("Edit")];
@@ -1383,6 +1385,21 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
     }
 }
 
+- (IBAction)savePlayQueueToLibrary:(id)sender
+{
+    // Check if there are items in the play queue
+    if (_playQueueController.playQueueModel.numberOfPlayQueueItems == 0) {
+        NSAlert * const alert = [[NSAlert alloc] init];
+        alert.messageText = _NS("Play Queue is Empty");
+        alert.informativeText = _NS("There are no items in the play queue to 
save.");
+        alert.alertStyle = NSAlertStyleWarning;
+        [alert runModal];
+        return;
+    }
+    
+    [VLCMain.sharedInstance.libraryController 
showCreatePlaylistDialogForPlayQueue];
+}
+
 - (IBAction)showConvertAndSave:(id)sender
 {
     [[VLCMain.sharedInstance convertAndSaveWindow] showWindow:self];
@@ -1900,6 +1917,8 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
 
     if (mi == nil) {
         return YES;
+    } else if (mi == self.savePlayqueueToLibrary) {
+        return _playQueueController.playQueueModel.numberOfPlayQueueItems > 0;
     } else if (mi == self.play) {
         return _playerController.playerState == VLC_PLAYER_STATE_PLAYING
             ? _playerController.currentMedia != nil && 
_playerController.pausable


=====================================
modules/gui/macosx/playqueue/VLCPlayQueueController.m
=====================================
@@ -24,6 +24,7 @@
 
 #import <vlc_interface.h>
 #import <vlc_player.h>
+#import <vlc_media_library.h>
 
 #import "extensions/NSString+Helpers.h"
 #import "main/VLCMain.h"


=====================================
modules/gui/macosx/playqueue/VLCPlayQueueMenuController.m
=====================================
@@ -26,6 +26,7 @@
 
 #import "extensions/NSString+Helpers.h"
 #import "extensions/NSMenu+VLCAdditions.h"
+#import "library/VLCLibraryController.h"
 #import "main/VLCMain.h"
 #import "playqueue/VLCPlayQueueController.h"
 #import "playqueue/VLCPlayQueueModel.h"
@@ -47,6 +48,7 @@
     NSMenuItem *_addFilesToPlayQueueMenuItem;
     NSMenuItem *_clearPlayQueueMenuItem;
     NSMenuItem *_sortMenuItem;
+    NSMenuItem *_createPlaylistMenuItem;
 }
 
 @property (readwrite, atomic) NSArray<NSMenuItem *> *items;
@@ -86,6 +88,9 @@
     _clearPlayQueueMenuItem = [[NSMenuItem alloc] initWithTitle:_NS("Clear 
Play Queue") action:@selector(clearPlayQueue:) keyEquivalent:@""];
     _clearPlayQueueMenuItem.target = self;
 
+    _createPlaylistMenuItem = [[NSMenuItem alloc] initWithTitle:_NS("Create 
Playlist from Queue") action:@selector(createPlaylistFromQueue:) 
keyEquivalent:@""];
+    _createPlaylistMenuItem.target = self;
+
     _playQueueSortingMenuController = [[VLCPlayQueueSortingMenuController 
alloc] init];
     _sortMenuItem = [[NSMenuItem alloc] initWithTitle:_NS("Sort Play Queue") 
action:nil keyEquivalent:@""];
     [_sortMenuItem 
setSubmenu:_playQueueSortingMenuController.playQueueSortingMenu];
@@ -98,6 +103,7 @@
         NSMenuItem.separatorItem,
         _addFilesToPlayQueueMenuItem,
         _clearPlayQueueMenuItem,
+        _createPlaylistMenuItem,
         _sortMenuItem
     ];
 
@@ -106,6 +112,7 @@
         NSMenuItem.separatorItem,
         _addFilesToPlayQueueMenuItem,
         _clearPlayQueueMenuItem,
+        _createPlaylistMenuItem,
         _sortMenuItem
     ];
 
@@ -199,6 +206,27 @@
     [_playQueueController clearPlayQueue];
 }
 
+- (void)createPlaylistFromQueue:(id)sender
+{
+    NSIndexSet * const selectedIndexes = 
self.playQueueTableView.selectedRowIndexes;
+    
+    NSArray<VLCPlayQueueItem *> *items = nil;
+    if (selectedIndexes.count > 0) {
+        NSMutableArray<VLCPlayQueueItem *> * const selectedItems = 
[NSMutableArray arrayWithCapacity:selectedIndexes.count];
+        [selectedIndexes enumerateIndexesUsingBlock:^(const NSUInteger idx, 
BOOL * const stop) {
+            VLCPlayQueueItem * const item = 
[_playQueueController.playQueueModel playQueueItemAtIndex:idx];
+            if (item) {
+                [selectedItems addObject:item];
+            }
+        }];
+        items = selectedItems.copy;
+    } else {
+        items = _playQueueController.playQueueModel.playQueueItems;
+    }
+    
+    [VLCMain.sharedInstance.libraryController 
showCreatePlaylistDialogForPlayQueueItems:items];
+}
+
 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
 {
     if (menuItem == _addFilesToPlayQueueMenuItem) {
@@ -207,6 +235,9 @@
     } else if (menuItem == _clearPlayQueueMenuItem) {
         return (self.playQueueTableView.numberOfRows > 0);
 
+    } else if (menuItem == _createPlaylistMenuItem) {
+        return (self.playQueueTableView.numberOfRows > 0);
+
     } else if (menuItem == _removeMenuItem ||
                menuItem == _playMenuItem ||
                menuItem == _informationMenuItem) {


=====================================
modules/gui/macosx/playqueue/VLCPlayQueueModel.h
=====================================
@@ -32,11 +32,12 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (readwrite, assign) VLCPlayQueueController *playQueueController;
 @property (readonly) NSUInteger numberOfPlayQueueItems;
+@property (readonly) NSArray<VLCPlayQueueItem *> *playQueueItems;
 
 - (void)dropExistingData;
 - (VLCPlayQueueItem *)playQueueItemAtIndex:(NSInteger)index;
-- (void)addItems:(NSArray *)array;
-- (void)addItems:(NSArray *)array atIndex:(size_t)index count:(size_t)count;
+- (void)addItems:(NSArray<VLCPlayQueueItem *> *)array;
+- (void)addItems:(NSArray<VLCPlayQueueItem *> *)array atIndex:(size_t)index 
count:(size_t)count;
 - (void)moveItemAtIndex:(size_t)index toTarget:(size_t)target;
 - (void)removeItemsInRange:(NSRange)range;
 - (void)updateItemAtIndex:(size_t)index;


=====================================
modules/gui/macosx/playqueue/VLCPlayQueueModel.m
=====================================
@@ -29,7 +29,7 @@
 
 @interface VLCPlayQueueModel ()
 {
-    NSMutableArray *_playQueueArray;
+    NSMutableArray<VLCPlayQueueItem *> *_playQueueArray;
 }
 @end
 
@@ -49,6 +49,11 @@
     return _playQueueArray.count;
 }
 
+- (NSArray<VLCPlayQueueItem *> *)playQueueItems
+{
+    return [_playQueueArray copy];
+}
+
 - (void)dropExistingData
 {
     [_playQueueArray removeAllObjects];
@@ -63,12 +68,12 @@
     return _playQueueArray[index];
 }
 
-- (void)addItems:(NSArray *)array
+- (void)addItems:(NSArray<VLCPlayQueueItem *> *)array
 {
     [_playQueueArray addObjectsFromArray:array];
 }
 
-- (void)addItems:(NSArray *)array atIndex:(size_t)index count:(size_t)count
+- (void)addItems:(NSArray<VLCPlayQueueItem *> *)array atIndex:(size_t)index 
count:(size_t)count
 {
     [_playQueueArray insertObjects:array atIndexes:[NSIndexSet 
indexSetWithIndexesInRange:NSMakeRange(index, count)]];
 }



View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/20cb70f22beb160eff022ae69ed3e23c373936f8...a64451e05b9bdd5b30a7ec43f9a8d5d361120f9b

-- 
View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/20cb70f22beb160eff022ae69ed3e23c373936f8...a64451e05b9bdd5b30a7ec43f9a8d5d361120f9b
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance
_______________________________________________
vlc-commits mailing list
vlc-commits@videolan.org
https://mailman.videolan.org/listinfo/vlc-commits

Reply via email to