[vlc-commits] [Git][videolan/vlc][master] 27 commits: macosx: Add VLCPlaybackEndView

François Cartegnie (@fcartegnie) gitlab at videolan.org
Tue May 20 07:06:24 UTC 2025



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
5546068d by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Add VLCPlaybackEndView

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
b9001b49 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Add VLCPlaybackEndViewController

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
16743f07 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Connect playback end view to playback end view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
64ce69a8 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Set up text for playback end view for translation

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
723efb15 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Add countdown mechanism to playback end view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
a8bb5bb0 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Override init in playback end view controller to use expected nib

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
49babb62 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Emit timeout notification in playback end view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
937552d9 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Add method to display playback end view in main video view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
b700ae0f by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Emit notification on playback end view timeout via main video view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
07afd043 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Wait for playback end view timeout before disabling video appearance in library window

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
30989a55 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Match displaying of library-related controls and labels in playback end view with its parent main video view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
dcb49d7f by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Use existing returnToLibrary method within video view controller for playback end timeout

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
ef116086 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Emit relevant notifications on button interaction in playback end view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
6e383808 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Handle return to library notification in main video view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
ef8ef50a by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Invalidate the ongoing timer when interacting with the playback end view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
dc80a193 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: React to return to library button from playback end view in main video view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
e3703a67 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Improve background for playback end view

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
3f4cc531 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Make playback end view inert in cases where user returns to library via other means than end view button

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
ae8b0485 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Ensure playback end view fits within video view bounds

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
a195362b by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Add convenience method to NSArray extension to get index of an ml item in an ml array

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
add38d76 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Add convenience creator method for VLCInputItem from NSURL

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
fc20e5e3 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Handle playqueue operation within playback end view

Limit playback end view controller to requesting view hide

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
f11f49a1 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Suggest playing next input item in folder in playback end view

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
cafe77c7 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Improve sizing and text layout in playback end view XIB

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
6bc98434 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Try to find relevant next media library item when current item is a media library media item in playback end view

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
ada32a3d by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Make end of playback view disable-able with setting

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
b65d5ca6 by Claudio Cambra at 2025-05-20T06:32:39+00:00
macosx: Set end of playback view by default to enabled

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -


18 changed files:

- extras/package/macosx/VLC.xcodeproj/project.pbxproj
- modules/gui/macosx/Makefile.am
- modules/gui/macosx/UI/SimplePreferences.xib
- + modules/gui/macosx/UI/VLCPlaybackEndView.xib
- modules/gui/macosx/extensions/NSArray+VLCAdditions.h
- modules/gui/macosx/extensions/NSArray+VLCAdditions.m
- modules/gui/macosx/library/VLCInputItem.h
- modules/gui/macosx/library/VLCInputItem.m
- modules/gui/macosx/library/VLCLibraryWindow.m
- modules/gui/macosx/main/VLCMain+OldPrefs.m
- modules/gui/macosx/main/VLCMain.h
- modules/gui/macosx/main/VLCMain.m
- modules/gui/macosx/preferences/VLCSimplePrefsController.h
- modules/gui/macosx/preferences/VLCSimplePrefsController.m
- + modules/gui/macosx/views/VLCPlaybackEndViewController.h
- + modules/gui/macosx/views/VLCPlaybackEndViewController.m
- modules/gui/macosx/windows/video/VLCMainVideoViewController.h
- modules/gui/macosx/windows/video/VLCMainVideoViewController.m


Changes:

=====================================
extras/package/macosx/VLC.xcodeproj/project.pbxproj
=====================================
@@ -122,6 +122,7 @@
 		536BFD1929B181E100BD0776 /* VLCMainVideoViewOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 536BFD1829B181E100BD0776 /* VLCMainVideoViewOverlayView.m */; };
 		536EFBF5295BCB8300F4CB13 /* VLCLibraryUIUnits.m in Sources */ = {isa = PBXBuildFile; fileRef = 536EFBF4295BCB8300F4CB13 /* VLCLibraryUIUnits.m */; };
 		536EFC39295E521600F4CB13 /* VLCLibraryVideoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 536EFC38295E521600F4CB13 /* VLCLibraryVideoViewController.m */; };
+		537040DF2DD848440030ABF5 /* VLCPlaybackEndViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 537040DE2DD848440030ABF5 /* VLCPlaybackEndViewController.m */; };
 		5377B15C2BD12EEE00D660B8 /* QuickLookThumbnailing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5377B15B2BD12EEE00D660B8 /* QuickLookThumbnailing.framework */; };
 		5377B15E2BD12F2900D660B8 /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5377B15D2BD12F2900D660B8 /* QuickLook.framework */; };
 		537976BA2A4319330036827E /* VLCSettingTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 537976B82A4319330036827E /* VLCSettingTextField.m */; };
@@ -296,6 +297,7 @@
 		1CFE8D561EA0D3D300E94451 /* ErrorPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ErrorPanel.xib; sourceTree = "<group>"; };
 		1CFE8D571EA0D42A00E94451 /* VLCErrorWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCErrorWindowController.h; sourceTree = "<group>"; };
 		1CFE8D581EA0D42A00E94451 /* VLCErrorWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCErrorWindowController.m; sourceTree = "<group>"; };
+		530429942DD1ED66004BB598 /* VLCPlaybackEndView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = VLCPlaybackEndView.xib; sourceTree = "<group>"; };
 		530771F22AEBB80C00656D3D /* VLCLibraryRepresentedItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCLibraryRepresentedItem.h; sourceTree = "<group>"; };
 		530771F32AEBBD5B00656D3D /* VLCLibraryRepresentedItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryRepresentedItem.m; sourceTree = "<group>"; };
 		5307A6F32967859F001E0C6A /* NSImage+VLCAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSImage+VLCAdditions.h"; sourceTree = "<group>"; };
@@ -406,6 +408,8 @@
 		536EFC37295E521600F4CB13 /* VLCLibraryVideoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryVideoViewController.h; sourceTree = "<group>"; };
 		536EFC38295E521600F4CB13 /* VLCLibraryVideoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryVideoViewController.m; sourceTree = "<group>"; };
 		536EFC3A295F828000F4CB13 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		537040DD2DD848440030ABF5 /* VLCPlaybackEndViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCPlaybackEndViewController.h; sourceTree = "<group>"; };
+		537040DE2DD848440030ABF5 /* VLCPlaybackEndViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCPlaybackEndViewController.m; sourceTree = "<group>"; };
 		5377B15B2BD12EEE00D660B8 /* QuickLookThumbnailing.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLookThumbnailing.framework; path = System/Library/PrivateFrameworks/QuickLookThumbnailing.framework; sourceTree = SDKROOT; };
 		5377B15D2BD12F2900D660B8 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; };
 		537976B82A4319330036827E /* VLCSettingTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCSettingTextField.m; sourceTree = "<group>"; };
@@ -1157,6 +1161,8 @@
 				535053ED2B610C3400D7EAF2 /* VLCLoadingOverlayView.m */,
 				53CBE3092C159A4D006BF2E3 /* VLCNoResultsLabel.h */,
 				53CBE30A2C159A4D006BF2E3 /* VLCNoResultsLabel.m */,
+				537040DD2DD848440030ABF5 /* VLCPlaybackEndViewController.h */,
+				537040DE2DD848440030ABF5 /* VLCPlaybackEndViewController.m */,
 				6B0AB0EC1F1AC8B3003A1B4E /* VLCPlaybackProgressSlider.h */,
 				6B0AB0ED1F1AC8B3003A1B4E /* VLCPlaybackProgressSlider.m */,
 				6B0AB0EE1F1AC8B3003A1B4E /* VLCPlaybackProgressSliderCell.h */,
@@ -2126,6 +2132,7 @@
 				534E8E3729A04F95009503F8 /* VLCMainVideoView.xib */,
 				7D67318322C8F3F60000AD40 /* VLCMediaSourceCollectionViewItem.xib */,
 				7DF14FBC2270CB1C0008ABE4 /* VLCMediaSourceDeviceCollectionViewItem.xib */,
+				530429942DD1ED66004BB598 /* VLCPlaybackEndView.xib */,
 				7D0F64002201F66D00FDB91F /* VLCPlayQueueTableCellView.xib */,
 				6B82241A1E4D2A9000833BE1 /* VLCStatusBarIconMainMenu.xib */,
 				7D713D332201BB130042BEB7 /* VLCLibraryWindow.xib */,
@@ -2272,6 +2279,7 @@
 				1CCC89022078A3D500E5626F /* ResumeDialog.xib in Sources */,
 				531062162CE259290087F863 /* VLCLibraryHomeViewActionButtonCell.m in Sources */,
 				532572032C3D79D80068DEC3 /* VLCLibrarySegmentBookmarkedLocation.m in Sources */,
+				537040DF2DD848440030ABF5 /* VLCPlaybackEndViewController.m in Sources */,
 				7DE7232E22A51F8D00D72616 /* VLCPositionFormatter.m in Sources */,
 				534E8E3A29A06325009503F8 /* VLCMainVideoViewController.m in Sources */,
 				531062112CE1CB520087F863 /* VLCLibraryHomeViewActionsView.m in Sources */,


=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -395,6 +395,8 @@ libmacosx_plugin_la_SOURCES = \
 	gui/macosx/views/VLCLoadingOverlayView.m \
 	gui/macosx/views/VLCNoResultsLabel.h \
 	gui/macosx/views/VLCNoResultsLabel.m \
+	gui/macosx/views/VLCPlaybackEndViewController.h \
+	gui/macosx/views/VLCPlaybackEndViewController.m \
 	gui/macosx/views/VLCPlaybackProgressSlider.h \
 	gui/macosx/views/VLCPlaybackProgressSlider.m \
 	gui/macosx/views/VLCPlaybackProgressSliderCell.h \
@@ -512,6 +514,7 @@ libmacosx_plugin_la_XIB_SOURCES = \
 	gui/macosx/UI/VLCMediaSourceCollectionViewItem.xib \
 	gui/macosx/UI/VLCMediaSourceDeviceCollectionViewItem.xib \
 	gui/macosx/UI/VLCInformationWindow.xib \
+	gui/macosx/UI/VLCPlaybackEndView.xib \
 	gui/macosx/UI/Open.xib \
 	gui/macosx/UI/PlaylistAccessoryView.xib \
 	gui/macosx/UI/PopupPanel.xib \


=====================================
modules/gui/macosx/UI/SimplePreferences.xib
=====================================
@@ -61,6 +61,7 @@
                 <outlet property="intf_artCheckbox" destination="3928" id="HND-nW-BHK"/>
                 <outlet property="intf_continueplaybackLabel" destination="3919" id="ZJO-qY-UCq"/>
                 <outlet property="intf_continueplaybackPopup" destination="3920" id="dZ3-SI-SCp"/>
+                <outlet property="intf_displayEndOfPlaybackViewCheckBox" destination="SYi-7I-cKE" id="sTC-Oc-1zX"/>
                 <outlet property="intf_enableNotificationsCheckbox" destination="3564" id="IIA-ce-g61"/>
                 <outlet property="intf_enableluahttpCheckbox" destination="lpZ-9y-Vfn" id="ieR-ZH-mcd"/>
                 <outlet property="intf_generalSettingsBox" destination="kup-aY-Wqq" id="RsL-yE-DVe"/>
@@ -375,13 +376,26 @@ Gw
                                     <action selector="interfaceSettingChanged:" target="-2" id="SxN-sI-55J"/>
                                 </connections>
                             </popUpButton>
+                            <button mirrorLayoutDirectionWhenInternationalizing="always" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SYi-7I-cKE">
+                                <rect key="frame" x="15" y="14" width="201" height="18"/>
+                                <buttonCell key="cell" type="check" title="Display end of playback view" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="Vsg-0v-H35">
+                                    <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+                                    <font key="font" metaFont="system"/>
+                                </buttonCell>
+                                <connections>
+                                    <action selector="interfaceSettingChanged:" target="-2" id="SR0-g4-Kx9"/>
+                                </connections>
+                            </button>
                         </subviews>
                         <constraints>
-                            <constraint firstAttribute="bottom" secondItem="3920" secondAttribute="bottom" constant="15" id="11b-WT-SyB"/>
+                            <constraint firstItem="SYi-7I-cKE" firstAttribute="leading" secondItem="gj4-zO-E26" secondAttribute="leading" constant="17" id="5SR-Aq-aAG"/>
+                            <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="SYi-7I-cKE" secondAttribute="trailing" constant="17" id="5a5-Eq-PCg"/>
                             <constraint firstAttribute="trailing" secondItem="3920" secondAttribute="trailing" constant="17" id="M7f-H3-4Mk"/>
                             <constraint firstItem="3919" firstAttribute="top" secondItem="gj4-zO-E26" secondAttribute="top" constant="12" id="R4h-Ob-XbD"/>
                             <constraint firstItem="3919" firstAttribute="centerY" secondItem="3920" secondAttribute="centerY" id="Txp-Vc-Bet"/>
                             <constraint firstItem="3919" firstAttribute="leading" secondItem="gj4-zO-E26" secondAttribute="leading" constant="17" id="cnI-H9-J0k"/>
+                            <constraint firstItem="SYi-7I-cKE" firstAttribute="top" secondItem="3919" secondAttribute="bottom" constant="10" id="czT-2q-qcN"/>
+                            <constraint firstAttribute="bottom" secondItem="SYi-7I-cKE" secondAttribute="bottom" constant="15" id="d1m-O6-Uxb"/>
                             <constraint firstItem="3920" firstAttribute="leading" secondItem="3919" secondAttribute="trailing" constant="17" id="gLu-nZ-tla"/>
                         </constraints>
                     </view>


=====================================
modules/gui/macosx/UI/VLCPlaybackEndView.xib
=====================================
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="VLCPlaybackEndViewController">
+            <connections>
+                <outlet property="countdownLabel" destination="C5J-Xc-NJc" id="tNP-DB-pOU"/>
+                <outlet property="largeTitleLabel" destination="jN5-TU-5uH" id="0qd-aL-HsE"/>
+                <outlet property="playNextItemButton" destination="YFo-7r-UzM" id="ZrY-Ot-LjJ"/>
+                <outlet property="restartPlayQueueButton" destination="CII-HN-I8g" id="uLY-jC-xYO"/>
+                <outlet property="returnToLibraryButton" destination="vpB-Yh-v5U" id="e37-g6-GGT"/>
+                <outlet property="view" destination="NcL-Nn-hUb" id="do2-eJ-8gU"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <visualEffectView wantsLayer="YES" blendingMode="withinWindow" material="sheet" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="NcL-Nn-hUb">
+            <rect key="frame" x="0.0" y="0.0" width="432" height="149"/>
+            <subviews>
+                <textField focusRingType="none" horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="jN5-TU-5uH">
+                    <rect key="frame" x="18" y="98" width="396" height="31"/>
+                    <textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Reached the end of the play queue" id="FXe-a6-cQO">
+                        <font key="font" textStyle="largeTitle" name=".SFNS-Regular"/>
+                        <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                        <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                    </textFieldCell>
+                </textField>
+                <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="C5J-Xc-NJc">
+                    <rect key="frame" x="18" y="72" width="396" height="16"/>
+                    <textFieldCell key="cell" alignment="center" title="Returning to library in X seconds" id="eII-yp-Ukx">
+                        <font key="font" textStyle="headline" name=".SFNS-Bold"/>
+                        <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                        <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                    </textFieldCell>
+                </textField>
+                <stackView distribution="fillEqually" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="huG-UY-8W9">
+                    <rect key="frame" x="20" y="20" width="392" height="32"/>
+                    <subviews>
+                        <button wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vpB-Yh-v5U">
+                            <rect key="frame" x="0.0" y="0.0" width="125" height="32"/>
+                            <buttonCell key="cell" type="inline" title="Return to library" bezelStyle="inline" image="books.vertical.fill" catalog="system" imagePosition="above" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ZPD-Ry-835">
+                                <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                                <font key="font" metaFont="smallSystemBold"/>
+                            </buttonCell>
+                        </button>
+                        <button wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CII-HN-I8g">
+                            <rect key="frame" x="133" y="0.0" width="126" height="32"/>
+                            <buttonCell key="cell" type="inline" title="Restart play queue" bezelStyle="inline" image="repeat" catalog="system" imagePosition="above" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="DVf-m9-gWa">
+                                <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                                <font key="font" metaFont="smallSystemBold"/>
+                            </buttonCell>
+                        </button>
+                        <button wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YFo-7r-UzM">
+                            <rect key="frame" x="267" y="0.0" width="125" height="32"/>
+                            <buttonCell key="cell" type="inline" title="Play next item" bezelStyle="inline" image="arrow.right" catalog="system" imagePosition="above" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="O5u-Ck-U6T">
+                                <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                                <font key="font" size="11" name=".SFNS-Semibold"/>
+                            </buttonCell>
+                        </button>
+                    </subviews>
+                    <visibilityPriorities>
+                        <integer value="1000"/>
+                        <integer value="1000"/>
+                        <integer value="1000"/>
+                    </visibilityPriorities>
+                    <customSpacing>
+                        <real value="3.4028234663852886e+38"/>
+                        <real value="3.4028234663852886e+38"/>
+                        <real value="3.4028234663852886e+38"/>
+                    </customSpacing>
+                </stackView>
+            </subviews>
+            <constraints>
+                <constraint firstItem="C5J-Xc-NJc" firstAttribute="centerX" secondItem="NcL-Nn-hUb" secondAttribute="centerX" id="6R0-qc-UGn"/>
+                <constraint firstItem="huG-UY-8W9" firstAttribute="leading" secondItem="jN5-TU-5uH" secondAttribute="leading" id="8qx-OA-DDQ"/>
+                <constraint firstItem="jN5-TU-5uH" firstAttribute="top" secondItem="NcL-Nn-hUb" secondAttribute="top" constant="20" id="Bae-6M-XVS"/>
+                <constraint firstItem="jN5-TU-5uH" firstAttribute="centerX" secondItem="NcL-Nn-hUb" secondAttribute="centerX" id="M51-kv-EP2"/>
+                <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="huG-UY-8W9" secondAttribute="trailing" constant="20" id="MbH-DN-TXI"/>
+                <constraint firstItem="huG-UY-8W9" firstAttribute="trailing" secondItem="jN5-TU-5uH" secondAttribute="trailing" id="Q7l-NQ-MIx"/>
+                <constraint firstItem="jN5-TU-5uH" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="NcL-Nn-hUb" secondAttribute="leading" constant="20" id="SWK-YP-EHJ"/>
+                <constraint firstItem="C5J-Xc-NJc" firstAttribute="trailing" secondItem="jN5-TU-5uH" secondAttribute="trailing" id="UgU-D1-V5D"/>
+                <constraint firstItem="C5J-Xc-NJc" firstAttribute="top" secondItem="jN5-TU-5uH" secondAttribute="bottom" constant="10" id="VgY-k7-miC"/>
+                <constraint firstItem="huG-UY-8W9" firstAttribute="top" secondItem="C5J-Xc-NJc" secondAttribute="bottom" constant="20" id="Wk4-Fh-M3Z"/>
+                <constraint firstItem="huG-UY-8W9" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="NcL-Nn-hUb" secondAttribute="leading" constant="20" id="X0f-L9-A1Y"/>
+                <constraint firstItem="C5J-Xc-NJc" firstAttribute="leading" secondItem="jN5-TU-5uH" secondAttribute="leading" id="Y4B-nN-ley"/>
+                <constraint firstItem="huG-UY-8W9" firstAttribute="centerX" secondItem="NcL-Nn-hUb" secondAttribute="centerX" id="mVr-Ig-tg3"/>
+                <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="jN5-TU-5uH" secondAttribute="trailing" constant="20" id="nWw-9e-tGN"/>
+                <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="149" id="owb-PL-E4y"/>
+                <constraint firstAttribute="bottom" secondItem="huG-UY-8W9" secondAttribute="bottom" constant="20" id="paw-6c-QHp"/>
+            </constraints>
+            <point key="canvasLocation" x="286" y="184.5"/>
+        </visualEffectView>
+    </objects>
+    <resources>
+        <image name="arrow.right" catalog="system" width="15" height="12"/>
+        <image name="books.vertical.fill" catalog="system" width="20" height="17"/>
+        <image name="repeat" catalog="system" width="18" height="14"/>
+    </resources>
+</document>


=====================================
modules/gui/macosx/extensions/NSArray+VLCAdditions.h
=====================================
@@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
 @interface NSArray (VLCAdditions)
 
 + (NSArray<VLCMediaLibraryMediaItem *> *)arrayFromVlcMediaList:(vlc_ml_media_list_t *)p_media_list;
+- (NSInteger)indexOfMediaLibraryItem:(id<VLCMediaLibraryItemProtocol>)mediaLibraryItem;
 
 @end
 


=====================================
modules/gui/macosx/extensions/NSArray+VLCAdditions.m
=====================================
@@ -40,4 +40,12 @@
     return mutableArray.copy;
 }
 
+- (NSInteger)indexOfMediaLibraryItem:(id<VLCMediaLibraryItemProtocol>)mediaLibraryItem
+{
+    return [self indexOfObjectPassingTest:^BOOL(const id<VLCMediaLibraryItemProtocol> item, const NSUInteger idx, BOOL * const stop) {
+        NSParameterAssert([item conformsToProtocol:@protocol(VLCMediaLibraryItemProtocol)]);
+        return item.libraryID == mediaLibraryItem.libraryID;
+    }];
+}
+
 @end


=====================================
modules/gui/macosx/library/VLCInputItem.h
=====================================
@@ -41,6 +41,7 @@ extern NSString * const VLCInputItemCommonDataDifferingFlagString;
 
 @interface VLCInputItem : NSObject<VLCLocallyManipulableItemProtocol>
 
++ (nullable instancetype)inputItemFromURL:(NSURL *)url;
 - (instancetype)initWithInputItem:(struct input_item_t *)p_inputItem;
 
 @property (readonly) struct input_item_t *vlcInputItem;


=====================================
modules/gui/macosx/library/VLCInputItem.m
=====================================
@@ -74,6 +74,18 @@ static const struct input_item_parser_cbs_t parserCallbacks =
 
 @implementation VLCInputItem
 
++ (nullable instancetype)inputItemFromURL:(NSURL *)url
+{
+    const char * const psz_uri = url.absoluteString.UTF8String;
+    const char * const psz_name = url.lastPathComponent.stringByDeletingPathExtension.UTF8String;
+    input_item_t * const p_input_item = input_item_New(psz_uri, psz_name);
+    if (p_input_item == NULL)
+        return nil;
+    VLCInputItem * const inputItem = [[VLCInputItem alloc] initWithInputItem:p_input_item];
+    input_item_Release(p_input_item);
+    return inputItem;
+}
+
 - (instancetype)initWithInputItem:(struct input_item_t *)p_inputItem
 {
     self = [super init];


=====================================
modules/gui/macosx/library/VLCLibraryWindow.m
=====================================
@@ -76,6 +76,7 @@
 #import "views/VLCImageView.h"
 #import "views/VLCLoadingOverlayView.h"
 #import "views/VLCNoResultsLabel.h"
+#import "views/VLCPlaybackEndViewController.h"
 #import "views/VLCRoundedCornerTextField.h"
 #import "views/VLCTrackingView.h"
 
@@ -412,7 +413,9 @@ static void addShadow(NSImageView *__unsafe_unretained imageView)
         VLCMediaLibraryMediaItem * const mediaItem = [VLCMediaLibraryMediaItem mediaItemForURL:currentMediaUrl];
         const BOOL decorativeViewVisible = mediaItem != nil && mediaItem.mediaType == VLC_ML_MEDIA_TYPE_AUDIO;
 
-        if (!decorativeViewVisible) {
+        if (!decorativeViewVisible && [NSUserDefaults.standardUserDefaults boolForKey:VLCPlaybackEndViewEnabledKey]) {
+            [self.videoViewController displayPlaybackEndView];
+        } else if (!decorativeViewVisible) {
             [self disableVideoPlaybackAppearance];
         }
     } else {


=====================================
modules/gui/macosx/main/VLCMain+OldPrefs.m
=====================================
@@ -34,7 +34,6 @@
 
 @implementation VLCMain(OldPrefs)
 
-static NSString * kVLCPreferencesVersion = @"VLCPreferencesVersion";
 static const int kCurrentPreferencesVersion = 4;
 
 + (void)initialize


=====================================
modules/gui/macosx/main/VLCMain.h
=====================================
@@ -42,6 +42,7 @@ intf_thread_t *getIntf(void);
 vlc_preparser_t *getNetworkPreparser();
 
 extern NSString *VLCConfigurationChangedNotification;
+extern NSString * const kVLCPreferencesVersion;
 
 /*****************************************************************************
  * VLCMain interface


=====================================
modules/gui/macosx/main/VLCMain.m
=====================================
@@ -73,6 +73,8 @@
 #import "preferences/prefs.h"
 #import "preferences/VLCSimplePrefsController.h"
 
+#import "views/VLCPlaybackEndViewController.h"
+
 #import "windows/VLCDetachedAudioWindow.h"
 #import "windows/VLCOpenWindowController.h"
 #import "windows/VLCOpenInputMetadata.h"
@@ -90,6 +92,8 @@ NSString *const kARM64UpdateURLString = @"https://update.videolan.org/vlc/sparkl
 
 NSString *VLCConfigurationChangedNotification = @"VLCConfigurationChangedNotification";
 
+NSString * const kVLCPreferencesVersion = @"VLCPreferencesVersion";
+
 #pragma mark -
 #pragma mark Private extension
 
@@ -308,7 +312,11 @@ static VLCMain *sharedInstance = nil;
     if (!_p_intf)
         return;
 
-    [self migrateOldPreferences];
+    NSUserDefaults * const defaults = NSUserDefaults.standardUserDefaults;
+    if ([defaults integerForKey:kVLCPreferencesVersion] != 4) {
+        [defaults setBool:YES forKey:VLCPlaybackEndViewEnabledKey];
+        [self migrateOldPreferences];
+    }
 
     _statusBarIcon = [[VLCStatusBarIcon alloc] init];
 


=====================================
modules/gui/macosx/preferences/VLCSimplePrefsController.h
=====================================
@@ -118,6 +118,7 @@
 @property (readwrite, weak) IBOutlet NSBox *intf_playbackControlBox;
 @property (readwrite, weak) IBOutlet NSTextField *intf_continueplaybackLabel;
 @property (readwrite, weak) IBOutlet NSPopUpButton *intf_continueplaybackPopup;
+ at property (readwrite, weak) IBOutlet NSButton *intf_displayEndOfPlaybackViewCheckBox;
 
 // intf pane - behaviour box
 @property (readwrite, weak) IBOutlet NSBox *intf_playbackBehaviourBox;


=====================================
modules/gui/macosx/preferences/VLCSimplePrefsController.m
=====================================
@@ -50,6 +50,7 @@
 #import "library/VLCLibraryController.h"
 #import "library/VLCLibraryModel.h"
 #import "library/VLCLibraryDataTypes.h"
+#import "views/VLCPlaybackEndViewController.h"
 
 static struct {
     const char iso[6];
@@ -400,6 +401,7 @@ create_toolbar_item(NSString *itemIdent, NSString *name, NSString *desc, NSStrin
 
     [_intf_playbackControlBox setTitle:_NS("Playback control")];
     [_intf_continueplaybackLabel setStringValue:_NS("Continue playback")];
+    [_intf_displayEndOfPlaybackViewCheckBox setTitle:_NS("Display end of playback view")];
     [_intf_statusIconCheckbox setTitle: _NS("Display VLC status menu icon")];
     [_intf_largeFontInListsCheckbox setTitle: _NS("Use large text for list views")];
 
@@ -608,7 +610,8 @@ create_toolbar_item(NSString *itemIdent, NSString *name, NSString *desc, NSStrin
      **********************/
     NSUInteger sel = 0;
     const char *pref = NULL;
-    pref = [[[NSUserDefaults standardUserDefaults] objectForKey:@"language"] UTF8String];
+    NSUserDefaults * const defaults = NSUserDefaults.standardUserDefaults;
+    pref = [[defaults objectForKey:@"language"] UTF8String];
     for (int x = 0; x < ARRAY_SIZE(language_map); x++) {
         [_intf_languagePopup addItemWithTitle:toNSStr(language_map[x].name)];
         if (pref) {
@@ -626,6 +629,8 @@ create_toolbar_item(NSString *itemIdent, NSString *name, NSString *desc, NSStrin
         [_intf_continueplaybackPopup setEnabled: YES];
     }
 
+    self.intf_displayEndOfPlaybackViewCheckBox.state = [defaults boolForKey:VLCPlaybackEndViewEnabledKey] ? NSControlStateValueOn : NSControlStateValueOff;
+
     [self setupButton:_intf_statusIconCheckbox forBoolValue: "macosx-statusicon"];
 
     [self setupButton:_video_nativeFullscreenCheckbox forBoolValue: "macosx-nativefullscreenmode"];
@@ -952,6 +957,9 @@ static inline void save_string_list(intf_thread_t * p_intf, id object, const cha
         [defaults setObject:toNSStr(language_map[index].iso) forKey:@"language"];
         [VLCSimplePrefsController updateRightToLeftSettings];
 
+        [defaults setBool:self.intf_displayEndOfPlaybackViewCheckBox.state == NSControlStateValueOn
+                   forKey:VLCPlaybackEndViewEnabledKey];
+
         config_PutInt("metadata-network-access", [_intf_artCheckbox state]);
 
         config_PutInt("macosx-statusicon", [_intf_statusIconCheckbox state]);


=====================================
modules/gui/macosx/views/VLCPlaybackEndViewController.h
=====================================
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ * VLCPlaybackEndViewController.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+extern NSString * const VLCPlaybackEndViewEnabledKey;
+extern NSString * const VLCPlaybackEndViewReturnToLibraryNotificationName;
+extern NSString * const VLCPlaybackEndViewHideNotificationName;
+
+ at interface VLCPlaybackEndViewController : NSViewController
+
+ at property (readwrite, weak) IBOutlet NSTextField *largeTitleLabel;
+ at property (readwrite, weak) IBOutlet NSTextField *countdownLabel;
+ at property (readwrite, weak) IBOutlet NSButton *returnToLibraryButton;
+ at property (readwrite, weak) IBOutlet NSButton *restartPlayQueueButton;
+ at property (readwrite, weak) IBOutlet NSButton *playNextItemButton;
+ at property (readwrite, nonatomic) BOOL hideLibraryControls;
+ at property (readonly) NSTimer *countdownTimer;
+
+- (void)startCountdown;
+
+ at end
+
+NS_ASSUME_NONNULL_END


=====================================
modules/gui/macosx/views/VLCPlaybackEndViewController.m
=====================================
@@ -0,0 +1,234 @@
+/*****************************************************************************
+ * VLCPlaybackEndViewController.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import "VLCPlaybackEndViewController.h"
+
+#import "main/VLCMain.h"
+#import "extensions/NSArray+VLCAdditions.h"
+#import "extensions/NSColor+VLCAdditions.h"
+#import "extensions/NSString+Helpers.h"
+#import "library/VLCInputItem.h"
+#import "library/VLCLibraryController.h"
+#import "library/VLCLibraryModel.h"
+#import "library/VLCLibraryUIUnits.h"
+#import "playqueue/VLCPlayQueueController.h"
+
+static const NSTimeInterval kVLCPlaybackEndTimeout = 10;
+static const NSTimeInterval kVLCPlaybackEndUpdateInterval = 0.1;
+
+NSString * const VLCPlaybackEndViewEnabledKey = @"VLCPlaybackEndViewEnabledKey";
+NSString * const VLCPlaybackEndViewHideNotificationName = @"VLCPlaybackEndViewRequestHide";
+NSString * const VLCPlaybackEndViewReturnToLibraryNotificationName = @"VLCPlaybackEndViewReturnToLibrary";
+
+ at interface VLCPlaybackEndViewController ()
+
+ at property NSDate *timeoutDate;
+ at property VLCInputItem *nextItem;
+
+ at end
+
+ at implementation VLCPlaybackEndViewController
+
+- (instancetype)init
+{
+    return [super initWithNibName:@"VLCPlaybackEndView" bundle:nil];
+}
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+    self.view.wantsLayer = YES;
+    self.view.layer.cornerRadius = VLCLibraryUIUnits.cornerRadius;
+    self.view.layer.borderColor = NSColor.VLCSubtleBorderColor.CGColor;
+    self.view.layer.borderWidth = VLCLibraryUIUnits.borderThickness;
+    self.largeTitleLabel.stringValue = _NS("Reached the end of the play queue");
+    self.returnToLibraryButton.stringValue = _NS("Return to library");
+    self.returnToLibraryButton.target = self;
+    self.returnToLibraryButton.action = @selector(returnToLibrary:);
+    self.restartPlayQueueButton.stringValue = _NS("Restart play queue");
+    self.restartPlayQueueButton.target = self;
+    self.restartPlayQueueButton.action = @selector(restartPlayQueue:);
+    self.playNextItemButton.stringValue = _NS("Play next item");
+    self.playNextItemButton.target = self;
+    self.playNextItemButton.action = @selector(playNextItem:);
+}
+
+- (void)startCountdown
+{
+    [self.countdownTimer invalidate];
+
+    VLCInputItem * const currentInputItem =
+        VLCMain.sharedInstance.playQueueController.currentlyPlayingInputItem;
+    NSURL * const inputItemUrl = [NSURL URLWithString:currentInputItem.MRL];
+    VLCMediaLibraryMediaItem * const mediaLibraryItem =
+        [VLCMediaLibraryMediaItem mediaItemForURL:inputItemUrl];
+
+    if (mediaLibraryItem) {
+        self.nextItem = [self nextItemForMediaLibraryItem:mediaLibraryItem];
+    } else {
+        self.nextItem = [self nextItemForInputItem:currentInputItem];
+    }
+    self.playNextItemButton.hidden = self.nextItem == nil;
+    self.timeoutDate = [NSDate dateWithTimeIntervalSinceNow:kVLCPlaybackEndTimeout];
+    _countdownTimer = [NSTimer scheduledTimerWithTimeInterval:kVLCPlaybackEndUpdateInterval
+                                                       target:self
+                                                     selector:@selector(handleUpdateInterval:)
+                                                     userInfo:nil
+                                                      repeats:YES];
+    [self handleUpdateInterval:nil];
+}
+
+- (nullable VLCInputItem *)nextItemForMediaLibraryItem:(VLCMediaLibraryMediaItem *)item
+{
+    NSArray<VLCMediaLibraryMediaItem *> *parentMediaItemCollection = nil;
+    __block NSInteger itemIndex = NSNotFound;
+
+    if (item.mediaSubType == VLC_ML_MEDIA_SUBTYPE_ALBUMTRACK) {
+        parentMediaItemCollection = [VLCMediaLibraryAlbum albumWithID:item.albumID].mediaItems;
+        itemIndex = [parentMediaItemCollection indexOfMediaLibraryItem:item];
+    } else if (item.mediaSubType == VLC_ML_MEDIA_SUBTYPE_SHOW_EPISODE) {
+        NSArray<VLCMediaLibraryShow *> * const shows =
+            VLCMain.sharedInstance.libraryController.libraryModel.listOfShows;
+        const NSInteger showContainingEpisodeIdx = [shows indexOfObjectPassingTest:^BOOL(VLCMediaLibraryShow * const show, const NSUInteger idx, BOOL * const stop) {
+            itemIndex = [show.mediaItems indexOfMediaLibraryItem:item];
+            return itemIndex != NSNotFound;
+        }];
+        if (showContainingEpisodeIdx != NSNotFound) {
+            parentMediaItemCollection = shows[showContainingEpisodeIdx].mediaItems;
+        }
+    }
+
+    if (parentMediaItemCollection == nil ||
+        itemIndex == NSNotFound ||
+        itemIndex + 1 >= parentMediaItemCollection.count
+    ) {
+        return [self nextItemForInputItem:item.inputItem];
+    }
+    return parentMediaItemCollection[itemIndex + 1].inputItem;
+}
+
+- (nullable VLCInputItem *)nextItemForInputItem:(VLCInputItem *)item
+{
+    if (item.isStream) {
+        return nil;
+    }
+
+    NSURL * const itemUrl = [NSURL URLWithString:item.MRL];
+    NSParameterAssert(itemUrl != nil);
+    NSString * const parentFolderPath = itemUrl.URLByDeletingLastPathComponent.path;
+    NSFileManager * const fm = NSFileManager.defaultManager;
+    NSError *error = nil;
+    NSArray<NSString *> * const itemSiblingItemPaths =
+        [fm contentsOfDirectoryAtPath:parentFolderPath error:&error];
+
+    if (error != nil) {
+        NSLog(@"Could not find siblings for item: %@\n\tReceived error:%@", item.decodedMRL, error.localizedDescription);
+        return nil;
+    }
+
+    const NSInteger itemIdx = [itemSiblingItemPaths indexOfObject:itemUrl.lastPathComponent];
+    NSParameterAssert(itemIdx != NSNotFound);
+    if (itemIdx + 1 >= itemSiblingItemPaths.count) {
+        NSLog(@"Played item was last in parent folder.");
+        return nil;
+    }
+    NSString * const nextItemFileName = itemSiblingItemPaths[itemIdx + 1];
+    NSURL * const nextItemURL =
+        [[NSURL fileURLWithPath:parentFolderPath] URLByAppendingPathComponent:nextItemFileName];
+    return [VLCInputItem inputItemFromURL:nextItemURL];
+}
+
+- (void)handleUpdateInterval:(nullable NSTimer *)timer
+{
+    NSDate * const now = NSDate.date;
+    NSDate * const timeout = self.timeoutDate;
+    const NSTimeInterval timeRemaining = [timeout timeIntervalSinceDate:now];
+    if (timeRemaining <= 0) {
+        if (self.nextItem) {
+            [self playNextItem:self];
+        } else {
+            [self returnToLibrary:self];
+        }
+        return;
+    }
+
+    NSString *remainingTimeString = @"";
+    if (@available(macOS 10.15, *)) {
+        NSRelativeDateTimeFormatter * const formatter = [[NSRelativeDateTimeFormatter alloc] init];
+        remainingTimeString = [formatter localizedStringForDate:timeout relativeToDate:now];
+    } else {
+        NSDateComponentsFormatter * const formatter = [[NSDateComponentsFormatter alloc] init];
+        NSString * const timeString = [formatter stringFromTimeInterval:timeRemaining];
+        remainingTimeString = [NSString stringWithFormat:_NS("in %@"), timeString];
+    }
+
+    NSString *countdownLabelString = nil;
+    if (self.nextItem) {
+        countdownLabelString =
+            [NSString stringWithFormat:_NS("Playing %@ %@"), self.nextItem.title, remainingTimeString];
+    } else {
+        countdownLabelString =
+            [NSString stringWithFormat:_NS("Returning to library %@"), remainingTimeString];
+    }
+    NSParameterAssert(countdownLabelString != nil);
+    self.countdownLabel.stringValue = countdownLabelString;
+}
+
+- (void)setHideLibraryControls:(BOOL)hideLibraryControls
+{
+    if (self.hideLibraryControls == hideLibraryControls)
+        return;
+    _hideLibraryControls = hideLibraryControls;
+    self.countdownLabel.hidden = hideLibraryControls;
+    self.returnToLibraryButton.hidden = hideLibraryControls;
+}
+
+- (void)returnToLibrary:(id)sender
+{
+    [self.countdownTimer invalidate];
+    [NSNotificationCenter.defaultCenter postNotificationName:VLCPlaybackEndViewReturnToLibraryNotificationName
+                                                      object:self];
+    [NSNotificationCenter.defaultCenter postNotificationName:VLCPlaybackEndViewHideNotificationName
+                                                      object:self];
+}
+
+- (void)restartPlayQueue:(id)sender
+{
+    [self.countdownTimer invalidate];
+    [VLCMain.sharedInstance.playQueueController playItemAtIndex:0];
+    [NSNotificationCenter.defaultCenter postNotificationName:VLCPlaybackEndViewHideNotificationName
+                                                      object:self];
+}
+
+- (void)playNextItem:(id)sender
+{
+    [self.countdownTimer invalidate];
+    if (self.nextItem == nil)
+        return;
+    [VLCMain.sharedInstance.playQueueController addInputItem:self.nextItem.vlcInputItem
+                                                  atPosition:-1
+                                               startPlayback:YES];
+    [NSNotificationCenter.defaultCenter postNotificationName:VLCPlaybackEndViewHideNotificationName
+                                                      object:self];
+}
+
+ at end


=====================================
modules/gui/macosx/windows/video/VLCMainVideoViewController.h
=====================================
@@ -27,6 +27,7 @@
 @class VLCMainVideoViewOverlayView;
 @class VLCMainVideoViewAudioMediaDecorativeView;
 @class VLCMainVideoViewControlsBar;
+ at class VLCPlaybackEndViewController;
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -64,10 +65,13 @@ NS_ASSUME_NONNULL_BEGIN
 @property (readonly) BOOL mouseOnControls;
 @property (readonly) BOOL pipIsActive;
 
+ at property (readonly) VLCPlaybackEndViewController *playbackEndViewController;
+
 - (void)showControls;
 - (void)hideControls;
 - (nullable NSView *)acquireVideoView;
 - (void)returnVideoView:(NSView *)videoView;
+- (void)displayPlaybackEndView;
 
 - (IBAction)togglePlayQueue:(id)sender;
 - (IBAction)returnToLibrary:(id)sender;


=====================================
modules/gui/macosx/windows/video/VLCMainVideoViewController.m
=====================================
@@ -39,6 +39,7 @@
 #import "playqueue/VLCPlayerController.h"
 
 #import "views/VLCBottomBarView.h"
+#import "views/VLCPlaybackEndViewController.h"
 
 #import "windows/controlsbar/VLCMainVideoViewControlsBar.h"
 
@@ -552,6 +553,10 @@
 
 - (IBAction)returnToLibrary:(id)sender
 {
+    if (self.playbackEndViewController) { // Stop any pending automatic return to library
+        [self.playbackEndViewController.countdownTimer invalidate];
+        [self.playbackEndViewController.view removeFromSuperview];
+    }
     VLCLibraryWindow *libraryWindow = (VLCLibraryWindow*)self.view.window;
     if (libraryWindow != nil) {
         [libraryWindow disableVideoPlaybackAppearance];
@@ -575,6 +580,41 @@
     [self applyAudioDecorativeViewForegroundCoverArtViewConstraints];
 }
 
+- (void)displayPlaybackEndView
+{
+    if (_playbackEndViewController == nil) {
+        _playbackEndViewController = [[VLCPlaybackEndViewController alloc] init];
+
+        NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
+        [defaultCenter addObserver:self
+                          selector:@selector(playbackEndViewReturnToLibrary:)
+                              name:VLCPlaybackEndViewReturnToLibraryNotificationName
+                            object:self.playbackEndViewController];
+        [defaultCenter addObserver:self
+                          selector:@selector(playbackEndViewHide:)
+                              name:VLCPlaybackEndViewHideNotificationName
+                            object:self.playbackEndViewController];
+    }
+
+    self.playbackEndViewController.hideLibraryControls = !self.displayLibraryControls;
+    [self.view addSubview:self.playbackEndViewController.view];
+    [self.playbackEndViewController.view.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES;
+    [self.playbackEndViewController.view.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES;
+    [self.playbackEndViewController.view.leadingAnchor constraintGreaterThanOrEqualToAnchor:self.view.leadingAnchor].active = YES;
+    [self.playbackEndViewController.view.trailingAnchor constraintLessThanOrEqualToAnchor:self.view.trailingAnchor].active = YES;
+    [self.playbackEndViewController startCountdown];
+}
+
+- (void)playbackEndViewReturnToLibrary:(NSNotification *)notification
+{
+    [self returnToLibrary:self];
+}
+
+- (void)playbackEndViewHide:(NSNotification *)notification
+{
+    [self.playbackEndViewController.view removeFromSuperview];
+}
+
 #pragma mark - PIPViewControllerDelegate
 
 - (BOOL)pipShouldClose:(PIPViewController *)pip



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/03514737e8afe2461efa7612726cb0d669644137...b65d5ca6c6bc70655b7fad09256e9088345f663e

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


VideoLAN code repository instance


More information about the vlc-commits mailing list