You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
622 lines
21 KiB
622 lines
21 KiB
/*****************************************************************************
|
|
* VLCInputManager.m: MacOS X interface module
|
|
*****************************************************************************
|
|
* Copyright (C) 2015-2018 VLC authors and VideoLAN
|
|
*
|
|
* 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 "VLCInputManager.h"
|
|
|
|
#import <MediaPlayer/MediaPlayer.h>
|
|
|
|
#include <vlc_url.h>
|
|
|
|
#import "coreinteraction/VLCCoreInteraction.h"
|
|
#import "main/CompatibilityFixes.h"
|
|
#import "main/VLCMain.h"
|
|
#import "menus/VLCMainMenu.h"
|
|
#import "os-integration/VLCRemoteControlService.h"
|
|
#import "os-integration/iTunes.h"
|
|
#import "os-integration/Spotify.h"
|
|
#import "panels/VLCPlaylistInfo.h"
|
|
#import "panels/VLCTrackSynchronizationWindowController.h"
|
|
#import "panels/dialogs/VLCResumeDialogController.h"
|
|
#import "windows/extensions/VLCExtensionsManager.h"
|
|
#import "windows/mainwindow/VLCMainWindow.h"
|
|
#import "windows/video/VLCVoutView.h"
|
|
|
|
|
|
@interface VLCInputManager()
|
|
- (void)updateMainMenu;
|
|
- (void)updateMainWindow;
|
|
- (void)updateMetaAndInfo;
|
|
- (void)updateDelays;
|
|
@end
|
|
|
|
#pragma mark Callbacks
|
|
|
|
static int InputThreadChanged(vlc_object_t *p_this, const char *psz_var,
|
|
vlc_value_t oldval, vlc_value_t new_val, void *param)
|
|
{
|
|
@autoreleasepool {
|
|
VLCInputManager *inputManager = (__bridge VLCInputManager *)param;
|
|
[inputManager performSelectorOnMainThread:@selector(inputThreadChanged) withObject:nil waitUntilDone:NO];
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static NSDate *lastPositionUpdate = nil;
|
|
|
|
static int InputEvent(vlc_object_t *p_this, const char *psz_var,
|
|
vlc_value_t oldval, vlc_value_t new_val, void *param)
|
|
{
|
|
@autoreleasepool {
|
|
VLCInputManager *inputManager = (__bridge VLCInputManager *)param;
|
|
|
|
switch (new_val.i_int) {
|
|
case INPUT_EVENT_STATE:
|
|
[inputManager performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject: nil waitUntilDone:NO];
|
|
break;
|
|
case INPUT_EVENT_RATE:
|
|
break;
|
|
case INPUT_EVENT_POSITION:
|
|
break;
|
|
case INPUT_EVENT_TITLE:
|
|
case INPUT_EVENT_CHAPTER:
|
|
[inputManager performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
|
|
break;
|
|
case INPUT_EVENT_CACHE:
|
|
[inputManager performSelectorOnMainThread:@selector(updateMainWindow) withObject:nil waitUntilDone:NO];
|
|
break;
|
|
case INPUT_EVENT_STATISTICS:
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[[[VLCMain sharedInstance] currentMediaInfoPanel] updateStatistics];
|
|
});
|
|
break;
|
|
case INPUT_EVENT_ES:
|
|
break;
|
|
case INPUT_EVENT_VOUT:
|
|
break;
|
|
case INPUT_EVENT_ITEM_META:
|
|
case INPUT_EVENT_ITEM_INFO:
|
|
[inputManager performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
|
|
[inputManager performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
|
|
[inputManager performSelectorOnMainThread:@selector(updateMetaAndInfo) withObject: nil waitUntilDone:NO];
|
|
break;
|
|
case INPUT_EVENT_BOOKMARK:
|
|
break;
|
|
case INPUT_EVENT_RECORD:
|
|
break;
|
|
case INPUT_EVENT_PROGRAM:
|
|
[inputManager performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
|
|
break;
|
|
case INPUT_EVENT_ITEM_EPG:
|
|
break;
|
|
case INPUT_EVENT_SIGNAL:
|
|
break;
|
|
|
|
case INPUT_EVENT_AUDIO_DELAY:
|
|
case INPUT_EVENT_SUBTITLE_DELAY:
|
|
[inputManager performSelectorOnMainThread:@selector(updateDelays) withObject:nil waitUntilDone:NO];
|
|
break;
|
|
|
|
case INPUT_EVENT_DEAD:
|
|
[inputManager performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark InputManager implementation
|
|
|
|
@interface VLCInputManager()
|
|
{
|
|
__weak VLCMain *o_main;
|
|
|
|
input_thread_t *p_current_input;
|
|
dispatch_queue_t informInputChangedQueue;
|
|
|
|
/* iTunes/Spotify play/pause support */
|
|
BOOL b_has_itunes_paused;
|
|
BOOL b_has_spotify_paused;
|
|
|
|
/* remote control support */
|
|
VLCRemoteControlService *_remoteControlService;
|
|
|
|
NSTimer *hasEndedTimer;
|
|
}
|
|
@end
|
|
|
|
@implementation VLCInputManager
|
|
|
|
+ (void)initialize
|
|
{
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSArray array], @"recentlyPlayedMediaList",
|
|
[NSDictionary dictionary], @"recentlyPlayedMedia", nil];
|
|
|
|
[defaults registerDefaults:appDefaults];
|
|
}
|
|
|
|
- (id)initWithMain:(VLCMain *)o_mainObj
|
|
{
|
|
self = [super init];
|
|
if(self) {
|
|
msg_Dbg(getIntf(), "Initializing input manager");
|
|
|
|
o_main = o_mainObj;
|
|
var_AddCallback(pl_Get(getIntf()), "input-current", InputThreadChanged, (__bridge void *)self);
|
|
|
|
informInputChangedQueue = dispatch_queue_create("org.videolan.vlc.inputChangedQueue", DISPATCH_QUEUE_SERIAL);
|
|
|
|
if (@available(macOS 10.12.2, *)) {
|
|
_remoteControlService = [[VLCRemoteControlService alloc] init];
|
|
[_remoteControlService subscribeToRemoteCommands];
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
* TODO: Investigate if this can be moved to dealloc again. Current problems:
|
|
* - dealloc might be never called of this object, as strong references could be in the
|
|
* (already stopped) main loop, preventing the refcount to go 0.
|
|
* - Calling var_DelCallback waits for all callbacks to finish. Thus, while dealloc is already
|
|
* called, callback might grab a reference to this object again, which could cause trouble.
|
|
*/
|
|
- (void)deinit
|
|
{
|
|
msg_Dbg(getIntf(), "Deinitializing input manager");
|
|
if (@available(macOS 10.12.2, *)) {
|
|
[_remoteControlService unsubscribeFromRemoteCommands];
|
|
}
|
|
|
|
if (p_current_input) {
|
|
/* continue playback where you left off */
|
|
[self storePlaybackPositionForItem:p_current_input];
|
|
|
|
var_DelCallback(p_current_input, "intf-event", InputEvent, (__bridge void *)self);
|
|
input_Release(p_current_input);
|
|
p_current_input = NULL;
|
|
}
|
|
|
|
var_DelCallback(pl_Get(getIntf()), "input-current", InputThreadChanged, (__bridge void *)self);
|
|
|
|
#if !OS_OBJECT_USE_OBJC
|
|
dispatch_release(informInputChangedQueue);
|
|
#endif
|
|
}
|
|
|
|
- (void)inputThreadChanged
|
|
{
|
|
if (p_current_input) {
|
|
var_DelCallback(p_current_input, "intf-event", InputEvent, (__bridge void *)self);
|
|
input_Release(p_current_input);
|
|
p_current_input = NULL;
|
|
|
|
[[o_main mainMenu] setRateControlsEnabled: NO];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
|
|
object:nil];
|
|
}
|
|
|
|
// Cancel pending resume dialogs
|
|
[[[VLCMain sharedInstance] resumeDialog] cancel];
|
|
|
|
input_thread_t *p_input_changed = NULL;
|
|
|
|
// object is hold here and released then it is dead
|
|
p_current_input = playlist_CurrentInput(pl_Get(getIntf()));
|
|
if (p_current_input) {
|
|
var_AddCallback(p_current_input, "intf-event", InputEvent, (__bridge void *)self);
|
|
[self playbackStatusUpdated];
|
|
[[o_main mainMenu] setRateControlsEnabled: YES];
|
|
|
|
if ([o_main activeVideoPlayback] && [[[o_main mainWindow] videoView] isHidden]) {
|
|
[[o_main mainWindow] changePlaylistState: psPlaylistItemChangedEvent];
|
|
}
|
|
|
|
p_input_changed = input_Hold(p_current_input);
|
|
|
|
// [[o_main playlist] currentlyPlayingItemChanged];
|
|
|
|
[self continuePlaybackWhereYouLeftOff:p_current_input];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
|
|
object:nil];
|
|
}
|
|
|
|
[self updateMetaAndInfo];
|
|
|
|
[self updateMainWindow];
|
|
[self updateDelays];
|
|
[self updateMainMenu];
|
|
|
|
/*
|
|
* Due to constraints within NSAttributedString's main loop runtime handling
|
|
* and other issues, we need to inform the extension manager on a separate thread.
|
|
* The serial queue ensures that changed inputs are propagated in the same order as they arrive.
|
|
*/
|
|
dispatch_async(informInputChangedQueue, ^{
|
|
[[self->o_main extensionsManager] inputChanged:p_input_changed];
|
|
if (p_input_changed)
|
|
input_Release(p_input_changed);
|
|
});
|
|
}
|
|
|
|
- (void)playbackStatusUpdated
|
|
{
|
|
// On shutdown, input might not be dead yet. Cleanup actions like itunes playback
|
|
// and playback positon are done in different code paths (dealloc and appWillTerminate:).
|
|
if ([[VLCMain sharedInstance] isTerminating]) {
|
|
return;
|
|
}
|
|
|
|
int64_t state = -1;
|
|
if (p_current_input) {
|
|
state = var_GetInteger(p_current_input, "state");
|
|
}
|
|
|
|
// cancel itunes timer if next item starts playing
|
|
if (state > -1 && state != END_S) {
|
|
if (hasEndedTimer) {
|
|
[hasEndedTimer invalidate];
|
|
hasEndedTimer = nil;
|
|
}
|
|
}
|
|
|
|
if (state == PLAYING_S) {
|
|
[self stopItunesPlayback];
|
|
|
|
[[o_main mainMenu] setPause];
|
|
[[o_main mainWindow] setPause];
|
|
|
|
if (@available(macOS 10.12.2, *)) {
|
|
[MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStatePlaying;
|
|
}
|
|
} else {
|
|
[[o_main mainMenu] setSubmenusEnabled: FALSE];
|
|
[[o_main mainMenu] setPlay];
|
|
[[o_main mainWindow] setPlay];
|
|
|
|
if (state == PAUSE_S) {
|
|
|
|
if (@available(macOS 10.12.2, *)) {
|
|
[MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStatePaused;
|
|
}
|
|
}
|
|
|
|
if (state == END_S || state == -1) {
|
|
/* continue playback where you left off */
|
|
if (p_current_input)
|
|
[self storePlaybackPositionForItem:p_current_input];
|
|
|
|
if (hasEndedTimer) {
|
|
[hasEndedTimer invalidate];
|
|
}
|
|
hasEndedTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5
|
|
target: self
|
|
selector: @selector(onPlaybackHasEnded:)
|
|
userInfo: nil
|
|
repeats: NO];
|
|
|
|
if (@available(macOS 10.12.2, *)) {
|
|
[MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStateStopped;
|
|
}
|
|
}
|
|
}
|
|
|
|
[self updateMainWindow];
|
|
[self sendDistributedNotificationWithUpdatedPlaybackStatus];
|
|
}
|
|
|
|
// Called when playback has ended and likely no subsequent media will start playing
|
|
- (void)onPlaybackHasEnded:(id)sender
|
|
{
|
|
msg_Dbg(getIntf(), "Playback has been ended");
|
|
|
|
[self resumeItunesPlayback];
|
|
hasEndedTimer = nil;
|
|
}
|
|
|
|
- (void)stopItunesPlayback
|
|
{
|
|
intf_thread_t *p_intf = getIntf();
|
|
int64_t controlItunes = var_InheritInteger(p_intf, "macosx-control-itunes");
|
|
if (controlItunes <= 0)
|
|
return;
|
|
|
|
// pause iTunes
|
|
if (!b_has_itunes_paused) {
|
|
iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
|
|
if (iTunesApp && [iTunesApp isRunning]) {
|
|
if ([iTunesApp playerState] == iTunesEPlSPlaying) {
|
|
msg_Dbg(p_intf, "pausing iTunes");
|
|
[iTunesApp pause];
|
|
b_has_itunes_paused = YES;
|
|
}
|
|
}
|
|
}
|
|
|
|
// pause Spotify
|
|
if (!b_has_spotify_paused) {
|
|
SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
|
|
|
|
if (spotifyApp) {
|
|
if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
|
|
if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePlaying) {
|
|
msg_Dbg(p_intf, "pausing Spotify");
|
|
[spotifyApp pause];
|
|
b_has_spotify_paused = YES;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)resumeItunesPlayback
|
|
{
|
|
intf_thread_t *p_intf = getIntf();
|
|
if (var_InheritInteger(p_intf, "macosx-control-itunes") > 1) {
|
|
if (b_has_itunes_paused) {
|
|
iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
|
|
if (iTunesApp && [iTunesApp isRunning]) {
|
|
if ([iTunesApp playerState] == iTunesEPlSPaused) {
|
|
msg_Dbg(p_intf, "unpausing iTunes");
|
|
[iTunesApp playpause];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (b_has_spotify_paused) {
|
|
SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
|
|
if (spotifyApp) {
|
|
if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
|
|
if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePaused) {
|
|
msg_Dbg(p_intf, "unpausing Spotify");
|
|
[spotifyApp play];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
b_has_itunes_paused = NO;
|
|
b_has_spotify_paused = NO;
|
|
}
|
|
|
|
- (void)updateMetaAndInfo
|
|
{
|
|
if (!p_current_input) {
|
|
[[[VLCMain sharedInstance] currentMediaInfoPanel] updatePanelWithItem:nil];
|
|
return;
|
|
}
|
|
|
|
input_item_t *p_input_item = input_GetItem(p_current_input);
|
|
|
|
// FIXME: update metadata in playlist model if needed
|
|
// [[[o_main playlist] model] updateItem:p_input_item];
|
|
[[[VLCMain sharedInstance] currentMediaInfoPanel] updatePanelWithItem:p_input_item];
|
|
|
|
if (!p_input_item) {
|
|
return;
|
|
}
|
|
|
|
if (@available(macOS 10.12.2, *)) {
|
|
NSMutableDictionary *currentlyPlayingTrackInfo = [NSMutableDictionary dictionary];
|
|
|
|
currentlyPlayingTrackInfo[MPMediaItemPropertyPlaybackDuration] = @(SEC_FROM_VLC_TICK(input_item_GetDuration(p_input_item)));
|
|
currentlyPlayingTrackInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(var_GetInteger(p_current_input, "time"));
|
|
currentlyPlayingTrackInfo[MPNowPlayingInfoPropertyPlaybackRate] = @(var_GetFloat(p_current_input, "rate"));
|
|
|
|
char *psz_title = input_item_GetTitle(p_input_item);
|
|
if (!psz_title)
|
|
psz_title = input_item_GetName(p_input_item);
|
|
currentlyPlayingTrackInfo[MPMediaItemPropertyTitle] = toNSStr(psz_title);
|
|
FREENULL(psz_title);
|
|
|
|
char *psz_artist = input_item_GetArtist(p_input_item);
|
|
currentlyPlayingTrackInfo[MPMediaItemPropertyArtist] = toNSStr(psz_artist);
|
|
FREENULL(psz_artist);
|
|
|
|
char *psz_album = input_item_GetAlbum(p_input_item);
|
|
currentlyPlayingTrackInfo[MPMediaItemPropertyAlbumTitle] = toNSStr(psz_album);
|
|
FREENULL(psz_album);
|
|
|
|
char *psz_track_number = input_item_GetTrackNumber(p_input_item);
|
|
currentlyPlayingTrackInfo[MPMediaItemPropertyAlbumTrackNumber] = @([toNSStr(psz_track_number) intValue]);
|
|
FREENULL(psz_track_number);
|
|
|
|
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = currentlyPlayingTrackInfo;
|
|
}
|
|
}
|
|
|
|
- (void)updateMainWindow
|
|
{
|
|
[[o_main mainWindow] updateWindow];
|
|
}
|
|
|
|
- (void)updateName
|
|
{
|
|
[[o_main mainWindow] updateName];
|
|
}
|
|
|
|
- (void)updateDelays
|
|
{
|
|
[[[VLCMain sharedInstance] trackSyncPanel] updateValues];
|
|
}
|
|
|
|
- (void)updateMainMenu
|
|
{
|
|
[[o_main mainMenu] setupMenus];
|
|
[[VLCCoreInteraction sharedInstance] resetAtoB];
|
|
}
|
|
|
|
- (void)sendDistributedNotificationWithUpdatedPlaybackStatus
|
|
{
|
|
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"VLCPlayerStateDidChange"
|
|
object:nil
|
|
userInfo:nil
|
|
deliverImmediately:YES];
|
|
}
|
|
|
|
- (BOOL)hasInput
|
|
{
|
|
return p_current_input != NULL;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Resume logic
|
|
|
|
|
|
- (BOOL)isValidResumeItem:(input_item_t *)p_item
|
|
{
|
|
char *psz_url = input_item_GetURI(p_item);
|
|
NSString *urlString = toNSStr(psz_url);
|
|
free(psz_url);
|
|
|
|
if ([urlString isEqualToString:@""])
|
|
return NO;
|
|
|
|
NSURL *url = [NSURL URLWithString:urlString];
|
|
|
|
if (![url isFileURL])
|
|
return NO;
|
|
|
|
BOOL isDir = false;
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir])
|
|
return NO;
|
|
|
|
if (isDir)
|
|
return NO;
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)continuePlaybackWhereYouLeftOff:(input_thread_t *)p_input_thread
|
|
{
|
|
NSDictionary *recentlyPlayedFiles = [[NSUserDefaults standardUserDefaults] objectForKey:@"recentlyPlayedMedia"];
|
|
if (!recentlyPlayedFiles)
|
|
return;
|
|
|
|
input_item_t *p_item = input_GetItem(p_input_thread);
|
|
if (!p_item)
|
|
return;
|
|
|
|
/* allow the user to over-write the start/stop/run-time */
|
|
if (var_GetFloat(p_input_thread, "run-time") > 0 ||
|
|
var_GetFloat(p_input_thread, "start-time") > 0 ||
|
|
var_GetFloat(p_input_thread, "stop-time") != 0) {
|
|
return;
|
|
}
|
|
|
|
/* check for file existance before resuming */
|
|
if (![self isValidResumeItem:p_item])
|
|
return;
|
|
|
|
char *psz_url = vlc_uri_decode(input_item_GetURI(p_item));
|
|
if (!psz_url)
|
|
return;
|
|
NSString *url = toNSStr(psz_url);
|
|
free(psz_url);
|
|
|
|
NSNumber *lastPosition = [recentlyPlayedFiles objectForKey:url];
|
|
if (!lastPosition || lastPosition.intValue <= 0)
|
|
return;
|
|
|
|
int settingValue = (int)config_GetInt("macosx-continue-playback");
|
|
if (settingValue == 2) // never resume
|
|
return;
|
|
|
|
CompletionBlock completionBlock = ^(enum ResumeResult result) {
|
|
|
|
if (result == RESUME_RESTART)
|
|
return;
|
|
|
|
vlc_tick_t lastPos = vlc_tick_from_sec( lastPosition.intValue );
|
|
msg_Dbg(getIntf(), "continuing playback at %lld", lastPos);
|
|
var_SetInteger(p_input_thread, "time", lastPos);
|
|
};
|
|
|
|
if (settingValue == 1) { // always
|
|
completionBlock(RESUME_NOW);
|
|
return;
|
|
}
|
|
|
|
[[[VLCMain sharedInstance] resumeDialog] showWindowWithItem:p_item
|
|
withLastPosition:lastPosition.intValue
|
|
completionBlock:completionBlock];
|
|
|
|
}
|
|
|
|
- (void)storePlaybackPositionForItem:(input_thread_t *)p_input_thread
|
|
{
|
|
if (!var_InheritBool(getIntf(), "macosx-recentitems"))
|
|
return;
|
|
|
|
input_item_t *p_item = input_GetItem(p_input_thread);
|
|
if (!p_item)
|
|
return;
|
|
|
|
if (![self isValidResumeItem:p_item])
|
|
return;
|
|
|
|
char *psz_url = vlc_uri_decode(input_item_GetURI(p_item));
|
|
if (!psz_url)
|
|
return;
|
|
NSString *url = toNSStr(psz_url);
|
|
free(psz_url);
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:@"recentlyPlayedMedia"]];
|
|
|
|
float relativePos = var_GetFloat(p_input_thread, "position");
|
|
long long pos = SEC_FROM_VLC_TICK(var_GetInteger(p_input_thread, "time"));
|
|
long long dur = SEC_FROM_VLC_TICK(input_item_GetDuration(p_item));
|
|
|
|
NSMutableArray *mediaList = [[defaults objectForKey:@"recentlyPlayedMediaList"] mutableCopy];
|
|
|
|
if (relativePos > .05 && relativePos < .95 && dur > 180) {
|
|
msg_Dbg(getIntf(), "Store current playback position of %f", relativePos);
|
|
[mutDict setObject:[NSNumber numberWithInteger:pos] forKey:url];
|
|
|
|
[mediaList removeObject:url];
|
|
[mediaList addObject:url];
|
|
NSUInteger mediaListCount = mediaList.count;
|
|
if (mediaListCount > 30) {
|
|
for (NSUInteger x = 0; x < mediaListCount - 30; x++) {
|
|
[mutDict removeObjectForKey:[mediaList firstObject]];
|
|
[mediaList removeObjectAtIndex:0];
|
|
}
|
|
}
|
|
} else {
|
|
[mutDict removeObjectForKey:url];
|
|
[mediaList removeObject:url];
|
|
}
|
|
[defaults setObject:mutDict forKey:@"recentlyPlayedMedia"];
|
|
[defaults setObject:mediaList forKey:@"recentlyPlayedMediaList"];
|
|
[defaults synchronize];
|
|
}
|
|
|
|
@end
|
|
|