Commit 0d649b9b authored by Marc Etcheverry's avatar Marc Etcheverry Committed by Felix Paul Kühne

Enhanced support for UPnP/DLNA video broadcast items with multiple resources...

Enhanced support for UPnP/DLNA video broadcast items with multiple resources and HDHomeRun detection
Signed-off-by: Felix Paul Kühne's avatarFelix Paul Kühne <fkuehne@videolan.org>
parent 3ed801aa
B"CHOOSE_AUDIO_TRACK"="Choose Audio Track";
......
/*****************************************************************************
* BasicUPnPDevice+VLC.h
* VLC for iOS
*****************************************************************************
* Copyright (c) 2014 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Marc Etcheverry <marc # taplightsoftware com>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
#import "BasicUPnPDevice.h"
/// Extension to detect HDHomeRun devices
@interface BasicUPnPDevice (VLC)
- (BOOL)VLC_isHDHomeRunMediaServer;
@end
/*****************************************************************************
* BasicUPnPDevice+VLC.m
* VLC for iOS
*****************************************************************************
* Copyright (c) 2014 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Marc Etcheverry <marc # taplightsoftware com>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
#import "BasicUPnPDevice+VLC.h"
@implementation BasicUPnPDevice (VLC)
/// Note that we check if it is a media server as well as being an HDHomeRun device
- (BOOL)VLC_isHDHomeRunMediaServer
{
/*
* This is a sample UPnP broadcast of a HDHomeRun HDTC-2US device:
*
* Type: urn:schemas-upnp-org:device:MediaServer:1
* UPnP Version: 1.0
* DLNA Device Class: DMS-1.50
* Friendly Name: HDHomeRun DLNA Tuner XXXXXXXX
* Manufacturer: Silicondust
* Manufacturer URL: http://www.silicondust.com
* Model Description: HDTC-2US HDHomeRun PLUS Tuner
* Model Name: HDHomeRun PLUS Tuner
* Model Number HDTC-2US
* Model URL: http://www.silicondust.com
* Serial Nuymber: XXXXXXXX
* Presentation URL: /
*
*/
// Let us do a case insensitive search to be safer. Silicondust's logo has is stylized as "SiliconDust", but the UPnP broadcast is "Silicondust"
if ([[self urn] rangeOfString:@"urn:schemas-upnp-org:device:MediaServer" options:NSCaseInsensitiveSearch].location != NSNotFound &&
[[self manufacturer] rangeOfString:@"Silicondust" options:NSCaseInsensitiveSearch].location != NSNotFound &&
[[self modelName] rangeOfString:@"HDHomeRun" options:NSCaseInsensitiveSearch].location != NSNotFound &&
[[self modelDescription] rangeOfString:@"HDHomeRun" options:NSCaseInsensitiveSearch].location != NSNotFound) {
return YES;
}
return NO;
}
@end
......@@ -26,6 +26,9 @@
@property (nonatomic, strong) IBOutlet VLCStatusLabel *statusLabel;
@property (nonatomic, readwrite) BOOL isDirectory;
/// When there is no subtitle content, you might want to enable this
@property (nonatomic, getter = isTitleLabelCentered) BOOL titleLabelCentered;
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *subtitle;
@property (nonatomic, retain) UIImage *icon;
......
......@@ -42,21 +42,29 @@
}
}
- (void)setTitleLabelCentered:(BOOL)titleLabelCentered
{
self.titleLabel.hidden = self.subtitleLabel.hidden = titleLabelCentered;
self.folderTitleLabel.hidden = !titleLabelCentered;
_titleLabelCentered = YES;
}
- (void)setIsDirectory:(BOOL)isDirectory
{
self.titleLabel.hidden = self.subtitleLabel.hidden = isDirectory;
self.folderTitleLabel.hidden = !isDirectory;
self.titleLabelCentered = isDirectory;
_isDirectory = isDirectory;
}
- (void)setTitle:(NSString *)title
{
BOOL isDir = self.isDirectory;
BOOL isDirOrCentered = self.isDirectory || [self isTitleLabelCentered];
self.folderTitleLabel.text = self.titleLabel.text = title;
self.titleLabel.hidden = self.subtitleLabel.hidden = isDir;
self.folderTitleLabel.hidden = !isDir;
self.titleLabel.hidden = self.subtitleLabel.hidden = isDirOrCentered;
self.folderTitleLabel.hidden = !isDirOrCentered;
_title = title;
}
......
......@@ -24,11 +24,12 @@
#import "WhiteRaccoon.h"
#import "NSString+SupportedMedia.h"
#import "VLCStatusLabel.h"
#import "BasicUPnPDevice+VLC.h"
#define kVLCServerTypeUPNP 0
#define kVLCServerTypeFTP 1
@interface VLCLocalServerFolderListViewController () <UITableViewDataSource, UITableViewDelegate, WRRequestDelegate, VLCLocalNetworkListCell, UISearchBarDelegate, UISearchDisplayDelegate>
@interface VLCLocalServerFolderListViewController () <UITableViewDataSource, UITableViewDelegate, WRRequestDelegate, VLCLocalNetworkListCell, UISearchBarDelegate, UISearchDisplayDelegate, UIActionSheetDelegate>
{
/* UI */
UIBarButtonItem *_backButton;
......@@ -53,6 +54,10 @@
NSMutableArray *_searchData;
UISearchBar *_searchBar;
UISearchDisplayController *_searchDisplayController;
/* UPnP items with multiple resources specifics */
MediaServer1ItemObject *_lastSelectedMediaItem;
UIView *_resourceSelectionActionSheetAnchorView;
}
@end
......@@ -219,12 +224,35 @@
if (mediaSize < 1)
mediaSize = (bitrate * durationInSeconds);
[cell setSubtitle: [NSString stringWithFormat:@"%@ (%@)", [NSByteCountFormatter stringFromByteCount:mediaSize countStyle:NSByteCountFormatterCountStyleFile], [VLCTime timeWithInt:durationInSeconds * 1000].stringValue]];
// object.item.videoItem.videoBroadcast items (like the HDHomeRun) may not have this information. Center the title (this makes channel names look better for the HDHomeRun)
if (mediaSize > 0 && durationInSeconds > 0) {
[cell setSubtitle: [NSString stringWithFormat:@"%@ (%@)", [NSByteCountFormatter stringFromByteCount:mediaSize countStyle:NSByteCountFormatterCountStyleFile], [VLCTime timeWithInt:durationInSeconds * 1000].stringValue]];
} else {
cell.titleLabelCentered = YES;
}
// Custom TV icon for video broadcasts
if ([[mediaItem objectClass] isEqualToString:@"object.item.videoItem.videoBroadcast"]) {
UIImage *broadcastImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
broadcastImage = [UIImage imageNamed:@"TVBroadcastIcon"];
} else {
broadcastImage = [UIImage imageNamed:@"TVBroadcastIcon~ipad"];
}
[cell setIcon:broadcastImage];
} else {
[cell setIcon:[UIImage imageNamed:@"blank"]];
}
[cell setIsDirectory:NO];
cell.isDownloadable = YES;
if (mediaItem.albumArt != nil)
[cell setIconURL:[NSURL URLWithString:mediaItem.albumArt]];
[cell setIcon:[UIImage imageNamed:@"blank"]];
// Disable downloading for the HDHomeRun for now to avoid infinite downloads (URI needs a duration parameter, otherwise you are just downloading a live stream). VLC also needs an extension in the file name for this to work.
if (![_UPNPdevice VLC_isHDHomeRunMediaServer]) {
cell.isDownloadable = YES;
}
cell.delegate = self;
} else {
[cell setIsDirectory:YES];
......@@ -303,18 +331,29 @@
NSUInteger count = uriCollectionKeys.count;
NSRange position;
NSUInteger correctIndex = 0;
NSUInteger numberOfDownloadableResources = 0;
for (NSUInteger i = 0; i < count; i++) {
position = [uriCollectionKeys[i] rangeOfString:@"http-get:*:video/"];
if (position.location != NSNotFound)
if (position.location != NSNotFound) {
correctIndex = i;
numberOfDownloadableResources++;
}
}
NSArray *uriCollectionObjects = [[mediaItem uriCollection] allValues];
if (uriCollectionObjects.count > 0)
itemURL = [NSURL URLWithString:uriCollectionObjects[correctIndex]];
if (itemURL) {
VLCAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate openMovieFromURL:itemURL];
// Present an action sheet for the user to choose which URI to download. Do not deselect the cell to provide visual feedback to the user
if (numberOfDownloadableResources > 1) {
_resourceSelectionActionSheetAnchorView = [tableView cellForRowAtIndexPath:indexPath];
[self presentResourceSelectionActionSheetForUPnPMediaItem:mediaItem forDownloading:NO];
} else {
if (uriCollectionObjects.count > 0) {
itemURL = [NSURL URLWithString:uriCollectionObjects[correctIndex]];
}
if (itemURL) {
VLCAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate openMovieFromURL:itemURL];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
}
} else if (_serverType == kVLCServerTypeFTP) {
......@@ -341,8 +380,160 @@
} else
[self _streamFTPFile:properObjectName];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
}
#pragma mark - UPnP Multiple Resources
/// Presents an UIActionSheet for the user to choose which <res> resource to play or download. Contains some display code specific to the HDHomeRun devices. Also parses "DLNA.ORG_PN" protocolInfo.
- (void)presentResourceSelectionActionSheetForUPnPMediaItem:(MediaServer1ItemObject *)mediaItem forDownloading:(BOOL)forDownloading {
NSParameterAssert(mediaItem);
if (!mediaItem) {
return;
}
// Store it so we can act on the action sheet callback.
_lastSelectedMediaItem = mediaItem;
NSArray *uriCollectionKeys = [[_lastSelectedMediaItem uriCollection] allKeys];
NSArray *uriCollectionObjects = [[_lastSelectedMediaItem uriCollection] allValues];
NSUInteger count = uriCollectionKeys.count;
NSRange position;
NSString *titleString;
if (!forDownloading) {
titleString = NSLocalizedString(@"SELECT_RESOURCE_TO_PLAY", nil);
} else {
titleString = NSLocalizedString(@"SELECT_RESOURCE_TO_DOWNLOAD", nil);
}
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:titleString
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
// Provide users with a descriptive action sheet for them to choose based on the multiple resources advertised by DLNA devices (HDHomeRun for example)
for (NSUInteger i = 0; i < count; i++) {
position = [uriCollectionKeys[i] rangeOfString:@"http-get:*:video/"];
if (position.location != NSNotFound) {
NSString *orgPNValue;
NSString *transcodeValue;
// Attempt to parse DLNA.ORG_PN first
NSString *protocolInfo = uriCollectionKeys[i];
NSArray *components = [protocolInfo componentsSeparatedByString:@";"];
NSArray *nonFlagsComponents = [components[0] componentsSeparatedByString:@":"];
NSString *orgPN = [nonFlagsComponents lastObject];
// Check to see if we are where we should be
NSRange orgPNRange = [orgPN rangeOfString:@"DLNA.ORG_PN="];
if (orgPNRange.location == 0) {
orgPNValue = [orgPN substringFromIndex:orgPNRange.length];
}
// HDHomeRun: Get the transcode profile from the HTTP API if possible
if ([_UPNPdevice VLC_isHDHomeRunMediaServer]) {
NSRange transcodeRange = [uriCollectionObjects[i] rangeOfString:@"transcode="];
if (transcodeRange.location != NSNotFound) {
transcodeValue = [uriCollectionObjects[i] substringFromIndex:transcodeRange.location + transcodeRange.length];
// Check that there are no more parameters
NSRange ampersandRange = [transcodeValue rangeOfString:@"&"];
if (ampersandRange.location != NSNotFound) {
transcodeValue = [transcodeValue substringToIndex:transcodeRange.location];
}
transcodeValue = [transcodeValue capitalizedString];
}
}
// Fallbacks to get the most descriptive resource title
NSString *profileTitle;
if ([transcodeValue length] && [orgPNValue length]) {
profileTitle = [NSString stringWithFormat:@"%@ (%@)", transcodeValue, orgPNValue];
// The extra whitespace is to get UIActionSheet to render the text better (this bug has been fixed in iOS 8)
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
if (!SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
profileTitle = [NSString stringWithFormat:@" %@ ", profileTitle];
}
}
} else if ([transcodeValue length]) {
profileTitle = transcodeValue;
} else if ([orgPNValue length]) {
profileTitle = orgPNValue;
} else if ([uriCollectionKeys[i] length]) {
profileTitle = uriCollectionKeys[i];
} else if ([uriCollectionObjects[i] length]) {
profileTitle = uriCollectionObjects[i];
} else {
profileTitle = NSLocalizedString(@"UNKNOWN", nil);
}
[actionSheet addButtonWithTitle:profileTitle];
}
}
// If no resources are found, an empty action sheet will be presented, but the fact that we got here implies that we have playable resources, so no special handling for this case is included
actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// Attach it to a specific view (a cell, a download button, etc)
if (_resourceSelectionActionSheetAnchorView) {
CGRect presentationRect = [self.view convertRect:_resourceSelectionActionSheetAnchorView.frame fromView:_resourceSelectionActionSheetAnchorView.superview];
[actionSheet showFromRect:presentationRect inView:self.view animated:YES];
} else {
[actionSheet showInView:self.view];
}
} else {
[actionSheet showInView:self.view];
}
}
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
// Act on the selected resource that the user selected
if (_lastSelectedMediaItem) {
if (buttonIndex != actionSheet.cancelButtonIndex) {
// Check again through our raw list which items are playable, since this same code was used to build the action sheet. Make sure we choose the right object based on the action sheet button index.
NSArray *uriCollectionKeys = [[_lastSelectedMediaItem uriCollection] allKeys];
NSArray *uriCollectionObjects = [[_lastSelectedMediaItem uriCollection] allValues];
if (uriCollectionObjects.count > 0) {
NSUInteger count = uriCollectionKeys.count;
NSMutableArray *possibleCollectionObjects = [[NSMutableArray alloc] initWithCapacity:[uriCollectionObjects count]];
for (NSUInteger i = 0; i < count; i++) {
if ([uriCollectionKeys[i] rangeOfString:@"http-get:*:video/"].location != NSNotFound) {
[possibleCollectionObjects addObject:uriCollectionObjects[i]];
}
}
NSString *itemURLString = uriCollectionObjects[buttonIndex];
if ([itemURLString length]) {
VLCAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate openMovieFromURL:[NSURL URLWithString:itemURLString]];
}
}
}
_lastSelectedMediaItem = nil;
_resourceSelectionActionSheetAnchorView = nil;
UITableView *activeTableView;
if ([self.searchDisplayController isActive]) {
activeTableView = self.searchDisplayController.searchResultsTableView;
} else {
activeTableView = self.tableView;
}
[activeTableView deselectRowAtIndexPath:[activeTableView indexPathForSelectedRow] animated:NO];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
#pragma mark - FTP specifics
......
......@@ -46,6 +46,11 @@
41F475D31899A438001B919F /* folderIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 41F475CF1899A438001B919F /* folderIcon@2x.png */; };
41F475D41899A438001B919F /* folderIcon@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 41F475D01899A438001B919F /* folderIcon@2x~ipad.png */; };
41F475D51899A438001B919F /* folderIcon~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 41F475D11899A438001B919F /* folderIcon~ipad.png */; };
493B1A1D195D06B1000A491A /* BasicUPnPDevice+VLC.m in Sources */ = {isa = PBXBuildFile; fileRef = 493B1A1C195D06B1000A491A /* BasicUPnPDevice+VLC.m */; };
493B1A21195D0EBE000A491A /* TVBroadcastIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 493B1A1E195D0EBE000A491A /* TVBroadcastIcon.png */; };
493B1A22195D0EBE000A491A /* TVBroadcastIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 493B1A1F195D0EBE000A491A /* TVBroadcastIcon@2x.png */; };
493B1A23195D0EBE000A491A /* TVBroadcastIcon@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 493B1A20195D0EBE000A491A /* TVBroadcastIcon@2x~ipad.png */; };
49CC60B6195F4F3F00ACB586 /* TVBroadcastIcon~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 49CC60B5195F4F3F00ACB586 /* TVBroadcastIcon~ipad.png */; };
7AC8629D1765DC560011611A /* style.css in Resources */ = {isa = PBXBuildFile; fileRef = 7AC8629B1765DC560011611A /* style.css */; };
7AC862A61765E9510011611A /* jquery-1.10.1.min.js in Resources */ = {isa = PBXBuildFile; fileRef = 7AC8629E1765E90C0011611A /* jquery-1.10.1.min.js */; };
7AC862A71765E9510011611A /* jquery.fileupload.js in Resources */ = {isa = PBXBuildFile; fileRef = 7AC8629F1765E90C0011611A /* jquery.fileupload.js */; };
......@@ -484,6 +489,12 @@
41F475CF1899A438001B919F /* folderIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folderIcon@2x.png"; sourceTree = "<group>"; };
41F475D01899A438001B919F /* folderIcon@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folderIcon@2x~ipad.png"; sourceTree = "<group>"; };
41F475D11899A438001B919F /* folderIcon~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folderIcon~ipad.png"; sourceTree = "<group>"; };
493B1A1B195D06B1000A491A /* BasicUPnPDevice+VLC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "BasicUPnPDevice+VLC.h"; path = "Sources/BasicUPnPDevice+VLC.h"; sourceTree = SOURCE_ROOT; };
493B1A1C195D06B1000A491A /* BasicUPnPDevice+VLC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "BasicUPnPDevice+VLC.m"; path = "Sources/BasicUPnPDevice+VLC.m"; sourceTree = SOURCE_ROOT; };
493B1A1E195D0EBE000A491A /* TVBroadcastIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TVBroadcastIcon.png; sourceTree = "<group>"; };
493B1A1F195D0EBE000A491A /* TVBroadcastIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "TVBroadcastIcon@2x.png"; sourceTree = "<group>"; };
493B1A20195D0EBE000A491A /* TVBroadcastIcon@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "TVBroadcastIcon@2x~ipad.png"; sourceTree = "<group>"; };
49CC60B5195F4F3F00ACB586 /* TVBroadcastIcon~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "TVBroadcastIcon~ipad.png"; sourceTree = "<group>"; };
7AC8629B1765DC560011611A /* style.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = style.css; sourceTree = "<group>"; };
7AC8629E1765E90C0011611A /* jquery-1.10.1.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "jquery-1.10.1.min.js"; sourceTree = "<group>"; };
7AC8629F1765E90C0011611A /* jquery.fileupload.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jquery.fileupload.js; sourceTree = "<group>"; };
......@@ -1943,6 +1954,10 @@
7D6B08C0174A831900A05173 /* vlc-xmas.png */,
7D6B08C1174A831900A05173 /* vlc-xmas@2x.png */,
7D3E6CE418589EE000D584E7 /* AppIcon512x512.png */,
493B1A1E195D0EBE000A491A /* TVBroadcastIcon.png */,
493B1A1F195D0EBE000A491A /* TVBroadcastIcon@2x.png */,
49CC60B5195F4F3F00ACB586 /* TVBroadcastIcon~ipad.png */,
493B1A20195D0EBE000A491A /* TVBroadcastIcon@2x~ipad.png */,
);
name = Icons;
sourceTree = "<group>";
......@@ -2243,6 +2258,8 @@
A7C3025A175A538700AD4388 /* Extensions */ = {
isa = PBXGroup;
children = (
493B1A1B195D06B1000A491A /* BasicUPnPDevice+VLC.h */,
493B1A1C195D06B1000A491A /* BasicUPnPDevice+VLC.m */,
7D3784C4183A9972009EE944 /* NSString+SupportedMedia.h */,
7D3784C5183A9972009EE944 /* NSString+SupportedMedia.m */,
7D3784C6183A9972009EE944 /* UIDevice+SpeedCategory.h */,
......@@ -2406,6 +2423,7 @@
7D63C1C1187767AF00BD5256 /* fsarrow-aspectratio.png in Resources */,
7D07F708175665ED00CDE21C /* navBarBackground@2x.png in Resources */,
7D07F709175665ED00CDE21C /* navBarBackground.png in Resources */,
493B1A22195D0EBE000A491A /* TVBroadcastIcon@2x.png in Resources */,
7D07F70C175669A800CDE21C /* button.png in Resources */,
7D53D51E187729EC00A1BA97 /* sidebar-cutout.png in Resources */,
7D07F70D175669A800CDE21C /* button@2x.png in Resources */,
......@@ -2431,6 +2449,7 @@
9BF07090185122AA009B23DD /* audio@2x~ipad.png in Resources */,
7D07F72F175691CC00CDE21C /* backButton@2x.png in Resources */,
7D32B384185E293D006CA474 /* Raleway.woff in Resources */,
49CC60B6195F4F3F00ACB586 /* TVBroadcastIcon~ipad.png in Resources */,
7D07F730175691CC00CDE21C /* backButtonHighlight.png in Resources */,
9BD3F368185DEC0A00569ADC /* arrows@2x.png in Resources */,
9BF0708E185122AA009B23DD /* audio.png in Resources */,
......@@ -2452,6 +2471,7 @@
41F475D21899A438001B919F /* folderIcon.png in Resources */,
7D47D6FE1760CD8700E86BAD /* slidermaximumTrack.png in Resources */,
7D53D519187729EC00A1BA97 /* sidebar@2x.png in Resources */,
493B1A23195D0EBE000A491A /* TVBroadcastIcon@2x~ipad.png in Resources */,
7D47D6FF1760CD8700E86BAD /* slidermaximumTrack@2x.png in Resources */,
7D47D7001760CD8700E86BAD /* slidermaxValue.png in Resources */,
7D47D7011760CD8700E86BAD /* slidermaxValue@2x.png in Resources */,
......@@ -2465,6 +2485,7 @@
7D63C1C5187767AF00BD5256 /* fsarrow-effects.png in Resources */,
7D47D7091760CD8700E86BAD /* playbackDoneButton.png in Resources */,
7D47D70A1760CD8700E86BAD /* playbackDoneButton@2x.png in Resources */,
493B1A21195D0EBE000A491A /* TVBroadcastIcon.png in Resources */,
7D47D70B1760CD8700E86BAD /* playbackDoneButtonHighlight.png in Resources */,
7D1B0682187DAF6A00F8BCD8 /* vlcsnap@2x.jpg in Resources */,
7D897874185DEF79009BAB5D /* repeat.png in Resources */,
......@@ -2736,6 +2757,7 @@
7D30F3CA183AB27A00FFC021 /* VLCDownloadViewController.m in Sources */,
7D30F3CD183AB29300FFC021 /* VLCMenuTableViewController.m in Sources */,
7D30F3D0183AB2AC00FFC021 /* VLCMediaFileDiscoverer.m in Sources */,
493B1A1D195D06B1000A491A /* BasicUPnPDevice+VLC.m in Sources */,
265D511D1922746C00E38383 /* VLCPlexParser.m in Sources */,
7D30F3D7183AB2F100FFC021 /* VLCLocalNetworkListCell.m in Sources */,
265D511C1922746C00E38383 /* VLCLocalPlexFolderListViewController.m in Sources */,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment