Spracherkennung für: .m vermutete Sprache: Masm {Masm[58] Cobol[52] ABAP[34]} [Methode: maximale Elemente, drei Dimensionen]
/*****************************************************************************
* VLCPlaybackEndViewController.m: MacOS X interface module
*****************************************************************************
* Copyright (C) 2025 VLC authors and VideoLAN
*
* Authors: Claudio Cambra <developer@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 "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 "main/VLCMain.h"
#import "playqueue/VLCPlayQueueController.h"
#import "views/VLCUIUnits.h"
#include <vlc_interface.h>
static const NSTimeInterval kVLCPlaybackEndTimeout = 10;
static const NSTimeInterval kVLCPlaybackEndUpdateInterval = 0.1;
NSString * const VLCPlaybackEndViewEnabledKey = @"VLCPlaybackEndViewEnabledKey";
NSString * const VLCPlaybackEndViewHideNotificationName = @"VLCPlaybackEndViewRequestHide";
NSString * const VLCPlaybackEndViewReturnToLibraryNotificationName = @"VLCPlaybackEndViewReturnToLibrary";
@interface VLCPlaybackEndViewController ()
@property NSDate *timeoutDate;
@property VLCInputItem *nextItem;
@end
@implementation VLCPlaybackEndViewController
- (instancetype)init
{
return [super initWithNibName:@"VLCPlaybackEndView" bundle:nil];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.wantsLayer = YES;
self.view.layer.cornerRadius = VLCUIUnits.cornerRadius;
self.view.layer.borderColor = NSColor.VLCSubtleBorderColor.CGColor;
self.view.layer.borderWidth = VLCUIUnits.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 __unused idx, BOOL * const __unused stop) {
itemIndex = [show.mediaItems indexOfMediaLibraryItem:item];
return itemIndex != NSNotFound;
}];
if (showContainingEpisodeIdx != NSNotFound) {
parentMediaItemCollection = shows[showContainingEpisodeIdx].mediaItems;
}
}
if (parentMediaItemCollection == nil ||
itemIndex == NSNotFound ||
(NSUInteger)(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;
}
NSArray<NSString *> * const playableExtensions = [NSString extensionsArrayFromVLCStyleString:EXTENSIONS_MEDIA];
NSArray<NSString *> * const itemPlayableSiblingItemPaths = [[itemSiblingItemPaths
sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString * const _Nonnull siblingItemPath, NSDictionary<NSString *, id> * const _Nullable __unused bindings) {
return [playableExtensions containsObject:siblingItemPath.pathExtension.lowercaseString];
}]];
const NSInteger itemIdx = [itemPlayableSiblingItemPaths indexOfObject:itemUrl.lastPathComponent];
NSParameterAssert(itemIdx != NSNotFound);
if ((NSUInteger)(itemIdx + 1) >= itemPlayableSiblingItemPaths.count) {
NSLog(@"Played item was last in parent folder.");
return nil;
}
NSString * const nextItemFileName = itemPlayableSiblingItemPaths[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];
}
@end
[Dauer der Verarbeitung: 0.2 Sekunden, vorverarbeitet 2026-10-24]