]> granicus.if.org Git - handbrake/commitdiff
Autoname creation date
authorMatthew Lazarow <2117435+MatthewLazarow@users.noreply.github.com>
Mon, 13 Aug 2018 19:56:01 +0000 (15:56 -0400)
committerScott <628593+sr55@users.noreply.github.com>
Mon, 13 Aug 2018 19:56:01 +0000 (20:56 +0100)
Adds {creation-date} and {creation-time} templates to autoname
preference option.

12 files changed:
gtk/configure.ac
gtk/src/callbacks.c
gtk/src/ghb.m4
macosx/HBPreferencesController.m
macosx/HBTitle.h
macosx/HBTitle.m
macosx/HBUtilities.h
macosx/HBUtilities.m
win/CS/HandBrakeWPF/Constants.cs
win/CS/HandBrakeWPF/Helpers/AutoNameHelper.cs
win/CS/HandBrakeWPF/Properties/Resources.Designer.cs
win/CS/HandBrakeWPF/Properties/Resources.resx

index fd196562e7f1eda4665a6266fcb0fd00c2946bac..fc2eee0c059dc7c72f40fd59d049e558a2520eae 100644 (file)
@@ -7,6 +7,7 @@ AM_INIT_AUTOMAKE([1.7.9 foreign dist-bzip2 dist-zip])
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 
+AC_USE_SYSTEM_EXTENSIONS
 AC_PROG_CC(gcc clang)
 AC_ISC_POSIX
 AC_PROG_CXX(g++ clang++)
index bee41d115a70b1c37dc8d29b391a943cea16f1c0..bf00b7a38b1f18c7dca6193fb3b85c567bf3c2ce 100644 (file)
@@ -50,6 +50,8 @@
 #include <netinet/in.h>
 #include <netdb.h>
 
+#include <regex.h>
+
 #if !defined(_NO_UPDATE_CHECK)
 #if defined(_OLD_WEBKIT)
 #include <webkit.h>
@@ -1050,6 +1052,71 @@ check_name_template(signal_user_data_t *ud, const char *str)
     return FALSE;
 }
 
+static int
+match_by_pattern(const char *string, const char *pattern)
+{
+    int status;
+    regex_t re;
+    if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
+    {
+        return 0;
+    }
+    status = regexec(&re, string, (size_t) 0, NULL, 0);
+    regfree(&re);
+    if (status != 0)
+    {
+        return 0;
+    }
+    return 1;
+}
+
+typedef struct {
+    const char *pattern;
+    const char *format;
+} datemap;
+
+static int
+parse_datestring(const char *src, struct tm *tm)
+{
+    datemap ymdThmsZ = {"[0-9]{4}-[0-1]?[0-9]-[0-3]?[0-9]T[0-9]{2}:[0-9]{2}:[0-9]{2}Z", "%Y-%m-%dT%H:%M:%SZ"};
+
+    datemap maps[1] = { ymdThmsZ };
+
+    for (int i = 0; i < sizeof(maps); i++)
+    {
+        if (match_by_pattern(src, maps[i].pattern))
+        {
+            strptime(src, maps[i].format, tm);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static char*
+get_creation_date(const char *pattern, const char *metaValue, const char *file)
+{
+    char date[11] = "";
+    if (metaValue != NULL && strlen(metaValue) > 1)
+    {
+        struct tm tm;
+        if (parse_datestring(metaValue, &tm))
+        {
+            strftime(date, 11, pattern, &tm);
+        }
+    }
+    else
+    {
+        struct stat stbuf;
+        if (g_stat(file, &stbuf) == 0){
+            struct tm *tm;
+            tm = localtime(&(stbuf.st_mtime));
+            strftime(date, 11, pattern, tm);
+        }
+    }
+    return strdup(date);
+}
+
 static void
 set_destination_settings(signal_user_data_t *ud, GhbValue *settings)
 {
@@ -1138,6 +1205,24 @@ set_destination_settings(signal_user_data_t *ud, GhbValue *settings)
                 g_string_append_printf(str, "%s", dt);
                 p += strlen("{date}");
             }
+            else if (!strncmp(p, "{creation-date}", strlen("{creation-date}")))
+            {
+                gchar *val;
+                const gchar *source = ghb_dict_get_string(ud->globals, "scan_source");
+                val = get_creation_date("%Y-%m-%d", ghb_dict_get_string(settings, "MetaReleaseDate"), source);
+                g_string_append_printf(str, "%s", val);
+                p += strlen("{creation-date}");
+                g_free(val);
+            }
+            else if (!strncmp(p, "{creation-time}", strlen("{creation-time}")))
+            {
+                gchar *val;
+                const gchar *source = ghb_dict_get_string(ud->globals, "scan_source");
+                val = get_creation_date("%H:%M", ghb_dict_get_string(settings, "MetaReleaseDate"), source);
+                g_string_append_printf(str, "%s", val);
+                p += strlen("{creation-time}");
+                g_free(val);
+            }
             else if (!strncmp(p, "{quality}", strlen("{quality}")))
             {
                 if (ghb_dict_get_bool(settings, "vquality_type_constant"))
index 26a0ed00a03df152a9e6285381d01db2ee81d57f..226da35cafe50bd678d1b563c590090650741fc9 100644 (file)
@@ -7412,7 +7412,7 @@ filter_output([
                             </child>
                             <child>
                               <object class="GtkEntry" id="auto_name_template">
-                                <property name="tooltip_text" translatable="yes">Available Options: {source} {title} {preset} {chapters} {date} {time} {quality} {bitrate}</property>
+                                <property name="tooltip_text" translatable="yes">Available Options: {source} {title} {preset} {chapters} {date} {time} {creation-date} {creation-time} {quality} {bitrate}</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
index aa02821f6ecc26552da5d8b5deff2b2dbf4c2dff..3f66bd71792ab80508f1be276d2d008948316220 100644 (file)
     [self.formatTokenField setTokenizingCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@"%%"]];
     [self.formatTokenField setCompletionDelay:0.2];
 
-    _buildInFormatTokens = @[@"{Source}", @"{Title}", @"{Date}", @"{Time}", @"{Chapters}", @"{Quality/Bitrate}"];
+    _buildInFormatTokens = @[@"{Source}", @"{Title}", @"{Date}", @"{Time}", @"{Creation-Date}", @"{Creation-Time}", @"{Chapters}", @"{Quality/Bitrate}"];
     [self.builtInTokenField setTokenizingCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@"%%"]];
     [self.builtInTokenField setStringValue:[self.buildInFormatTokens componentsJoinedByString:@"%%"]];
 
index 79869ea20217526ec4a0fbf943884f9eea968ded..3979b6d4c2f3e81078cebf50aa5722f8c3d719c2 100644 (file)
@@ -11,6 +11,21 @@ NS_ASSUME_NONNULL_BEGIN
 @class HBChapter;
 @class HBPreset;
 
+@interface HBMetadata : NSObject
+
+@property (nonatomic, readonly, nullable) NSString *name;
+@property (nonatomic, readonly, nullable) NSString *artist;
+@property (nonatomic, readonly, nullable) NSString *composer;
+@property (nonatomic, readonly, nullable) NSString *releaseDate;
+@property (nonatomic, readonly, nullable) NSString *comment;
+@property (nonatomic, readonly, nullable) NSString *album;
+@property (nonatomic, readonly, nullable) NSString *albumArtist;
+@property (nonatomic, readonly, nullable) NSString *genre;
+@property (nonatomic, readonly, nullable) NSString *description;
+@property (nonatomic, readonly, nullable) NSString *longDescription;
+
+@end
+
 /**
  * HBTitles is an interface to the low-level hb_title_t.
  * the properties are lazy-loaded.
@@ -46,6 +61,8 @@ NS_ASSUME_NONNULL_BEGIN
 @property (nonatomic, readonly) NSArray<NSDictionary<NSString *, id> *> *subtitlesTracks;
 @property (nonatomic, readonly) NSArray<HBChapter *> *chapters;
 
+@property (nonatomic, readonly) HBMetadata *metadata;
+
 @end
 
 NS_ASSUME_NONNULL_END
index e718624a7a6ec4bd9ba8aa05ad28d957e7e24891..01cf53bcae834ca8351f2a17a17344620c50c22e 100644 (file)
@@ -25,12 +25,37 @@ extern NSString *keySubTrackName;
 extern NSString *keySubTrackLanguageIsoCode;
 extern NSString *keySubTrackType;
 
+@interface HBMetadata ()
+
+@property (nonatomic, readonly) hb_metadata_t *hb_metadata;
+@property (nonatomic, copy) NSString *releaseDate;
+
+@end
+
+@implementation HBMetadata
+- (instancetype)initWithMetadata:(hb_metadata_t *)data
+{
+    _hb_metadata = data;
+    return self;
+}
+
+- (NSString *)releaseDate
+{
+    if (self.hb_metadata->release_date == nil)
+    {
+        return nil;
+    }
+    return @(self.hb_metadata->release_date);
+}
+
+@end
+
 @interface HBTitle ()
 
 @property (nonatomic, readonly) hb_title_t *hb_title;
 @property (nonatomic, readonly) hb_handle_t *hb_handle;
 @property (nonatomic, readwrite, copy) NSString *name;
-
+@property (nonatomic, readwrite) HBMetadata *metadata;
 @property (nonatomic, readwrite) NSArray *audioTracks;
 @property (nonatomic, readwrite) NSArray *subtitlesTracks;
 @property (nonatomic, readwrite) NSArray *chapters;
@@ -52,12 +77,14 @@ extern NSString *keySubTrackType;
         _hb_title = title;
         _hb_handle = handle;
         _featured = featured;
+
+        _metadata = [[HBMetadata alloc] initWithMetadata:title->metadata];
     }
 
     return self;
 }
 
-- (NSString *)name
+-  (NSString *)name
 {
     if (!_name)
     {
index 4b4fa035a2a6be5c103d3d9288e7ec4aacff49ef..fd8963693c376a0cb62003b7bb8ea27fe24edb67 100644 (file)
@@ -58,7 +58,8 @@ NS_ASSUME_NONNULL_BEGIN
                             chapters:(NSRange)chaptersRange
                              quality:(double)quality
                              bitrate:(int)bitrate
-                          videoCodec:(uint32_t)codec;
+                          videoCodec:(uint32_t)codec
+                        creationDate:(NSDate *)creationDate;
 
 + (NSString *)isoCodeForNativeLang:(NSString *)language;
 + (NSString *)iso6392CodeFor:(NSString *)language;
index 5794a712becf3dcd01aa2d0b1bd989377c2f660d..ae26584446f7e423a00b4c441c598b851074efb8 100644 (file)
     return mediaURL;
 }
 
++ (nullable NSDate *)getReleaseDate:(HBTitle *)title
+{
+    if ([title.metadata.releaseDate length] == 0){
+        return nil;
+    }
+    
+    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+    NSString *dt = title.metadata.releaseDate;
+    [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
+    NSDate *releaseDate = [dateFormatter dateFromString:dt];
+    return releaseDate;
+}
+
 + (NSString *)automaticNameForJob:(HBJob *)job
 {
     HBTitle *title = job.title;
+    
+    NSDate *releaseDate = [self getReleaseDate:title];
+    if (releaseDate == nil)
+    {
+        NSDictionary* fileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:job.fileURL.path error:nil];
+        releaseDate = [fileAttribs objectForKey:NSFileCreationDate];
+    }
 
     // Generate a new file name
     NSString *fileName = [HBUtilities automaticNameForSource:title.name
                                                     chapters:NSMakeRange(job.range.chapterStart + 1, job.range.chapterStop + 1)
                                                      quality:job.video.qualityType ? job.video.quality : 0
                                                      bitrate:!job.video.qualityType ? job.video.avgBitrate : 0
-                                                  videoCodec:job.video.encoder];
+                                                  videoCodec:job.video.encoder
+                                                creationDate:releaseDate
+                          ];
     return fileName;
 }
 
     // use the correct extension based on the container
     NSString *ext = [self automaticExtForJob:job];
     fileName = [fileName stringByAppendingPathExtension:ext];
-    
     return fileName;
 }
 
                              quality:(double)quality
                              bitrate:(int)bitrate
                           videoCodec:(uint32_t)codec
+                         creationDate:(NSDate *)creationDate
 {
     NSMutableString *name = [[NSMutableString alloc] init];
     // The format array contains the tokens as NSString
     NSArray *format = [[NSUserDefaults standardUserDefaults] objectForKey:@"HBAutoNamingFormat"];
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
 
     for (NSString *formatKey in format)
     {
         {
             NSDate *date = [NSDate date];
             NSString *dateString = nil;
-            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
             [formatter setDateStyle:NSDateFormatterShortStyle];
             [formatter setTimeStyle:NSDateFormatterNoStyle];
             dateString = [[formatter stringFromDate:date] stringByReplacingOccurrencesOfString:@"/" withString:@"-"];
         else if ([formatKey isEqualToString:@"{Time}"])
         {
             NSDate *date = [NSDate date];
-            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
             [formatter setDateStyle:NSDateFormatterNoStyle];
             [formatter setTimeStyle:NSDateFormatterShortStyle];
             [name appendString:[formatter stringFromDate:date]];
         }
+        else if ([formatKey isEqualToString:@"{Creation-Date}"])
+        {
+            NSString *dateString = nil;
+            [formatter setDateStyle:NSDateFormatterShortStyle];
+            [formatter setTimeStyle:NSDateFormatterNoStyle];
+            dateString = [[formatter stringFromDate:creationDate] stringByReplacingOccurrencesOfString:@"/" withString:@"-"];
+            [name appendString:dateString];
+
+        }
+        else if ([formatKey isEqualToString:@"{Creation-Time}"])
+        {
+            [formatter setDateStyle:NSDateFormatterNoStyle];
+            [formatter setTimeStyle:NSDateFormatterShortStyle];
+            [name appendString:[formatter stringFromDate:creationDate]];
+        }
         else if ([formatKey isEqualToString:@"{Chapters}"])
         {
             if (chaptersRange.location == chaptersRange.length)
index cf322e85ce3469263d3e9e94fcc1de33c2a30504..f13d8cb0fd04507e312c12799be2f4bc3eab50cf 100644 (file)
@@ -50,15 +50,25 @@ namespace HandBrakeWPF
         public const string Quality = "{quality}";\r
 \r
         /// <summary>\r
-        /// The quality.\r
+        /// The creation date of the new output file.\r
         /// </summary>\r
         public const string Date = "{date}";\r
 \r
         /// <summary>\r
-        /// The quality.\r
+        /// The creation time of the new output file.\r
         /// </summary>\r
         public const string Time = "{time}";\r
 \r
+        /// <summary>\r
+        /// The source creation date.\r
+        /// </summary>\r
+        public const string CretaionDate = "{creation-date}";\r
+\r
+        /// <summary>\r
+        /// The source creation time.\r
+        /// </summary>\r
+        public const string CreationTime = "{creation-time}";\r
+\r
         /// <summary>\r
         /// The bitrate.\r
         /// </summary>\r
index 4ef7405f24572e4d125e011878b9b208de6466bc..4fd7ec1fcd8f0a443efaea652a059ab6859eb15a 100644 (file)
@@ -18,6 +18,7 @@ namespace HandBrakeWPF.Helpers
     using HandBrake.Interop.Interop.Model.Encoding;\r
 \r
     using HandBrakeWPF.Extensions;\r
+    using HandBrakeWPF.Services.Encode.Model;\r
     using HandBrakeWPF.Services.Interfaces;\r
     using HandBrakeWPF.Services.Presets.Model;\r
 \r
@@ -86,6 +87,35 @@ namespace HandBrakeWPF.Helpers
                 if (chapterFinish != chapterStart && chapterFinish != string.Empty)\r
                     combinedChapterTag = chapterStart + "-" + chapterFinish;\r
 \r
+                // Local method to check if we have a creation time in the meta data. If not, fall back to source file creation time.\r
+                DateTime obtainCreateDateObject()\r
+                {\r
+                    var rd = task.MetaData.ReleaseDate;\r
+                    if (DateTime.TryParse(rd, out var d))\r
+                    {\r
+                        return d;\r
+                    }\r
+                    try\r
+                    {\r
+                        return File.GetCreationTime(task.Source);\r
+                    }\r
+                    catch (Exception e)\r
+                    {\r
+                        if (e is UnauthorizedAccessException ||\r
+                            e is PathTooLongException ||\r
+                            e is NotSupportedException)\r
+                        {\r
+                            // Suspect the most likely concerns trying to grab the creation date in which we would want to swallow exception.\r
+                            return default(DateTime);\r
+                        }\r
+                        throw e;                            \r
+                    }\r
+                }\r
+\r
+                var creationDateTime = obtainCreateDateObject();\r
+                string createDate = creationDateTime.Date.ToShortDateString().Replace('/', '-');\r
+                string createTime = creationDateTime.ToString("HH-mm"); \r
+\r
                 /*\r
                  * File Name\r
                  */\r
@@ -98,7 +128,9 @@ namespace HandBrakeWPF.Helpers
                             .Replace(Constants.Title, dvdTitle)\r
                             .Replace(Constants.Chapters, combinedChapterTag)\r
                             .Replace(Constants.Date, DateTime.Now.Date.ToShortDateString().Replace('/', '-'))\r
-                            .Replace(Constants.Time, DateTime.Now.ToString("HH:mm"));\r
+                            .Replace(Constants.Time, DateTime.Now.ToString("HH-mm"))\r
+                            .Replace(Constants.CretaionDate, createDate)\r
+                            .Replace(Constants.CreationTime, createTime);\r
                     // .Replace(Constants.Preset, sanitisedPresetName);\r
 \r
                     if (task.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality)\r
@@ -199,7 +231,7 @@ namespace HandBrakeWPF.Helpers
 \r
             return autoNamePath;\r
         }\r
-\r
+        \r
         /// <summary>\r
         /// Check if there is a valid autoname path.\r
         /// </summary>\r
@@ -219,6 +251,6 @@ namespace HandBrakeWPF.Helpers
             return userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNamePath).Trim().StartsWith("{source_path}") ||\r
                 (userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNamePath).Contains("{source_folder_name}") ||\r
                  Directory.Exists(userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNamePath).Trim()));\r
-        }\r
+        }        \r
     }\r
 }\r
index bbe866be0032e18c0d51c877a3f9d9d558568996..d5ccb157f36c1e1e0b9170036e9df14620894e19 100644 (file)
@@ -1242,7 +1242,7 @@ namespace HandBrakeWPF.Properties {
         ///   Looks up a localized string similar to The format of the output file. In addition to any supported file system character, you can use the following placeholders that will be replaced when you change title or scan a source.\r
         ///\r
         ///Live Update Options: {source} {title} {chapters} \r
-        ///Non-Live Options: {date} {time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters).\r
+        ///Non-Live Options: {date} {time} {creation-date} {creation-time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters).\r
         /// </summary>\r
         public static string Options_AdditionalFormatOptions {\r
             get {\r
index 21bed46f7bc7a53b20b7ff3bd04e3a5cc75af9cd..1c0e4c1f26ceb334c65b69eb69134fa8fccbeb6b 100644 (file)
@@ -479,7 +479,7 @@ Do you wish to proceed?</value>
     <value>The format of the output file. In addition to any supported file system character, you can use the following placeholders that will be replaced when you change title or scan a source.\r
 \r
 Live Update Options: {source} {title} {chapters} \r
-Non-Live Options: {date} {time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters)</value>\r
+Non-Live Options: {date} {time} {creation-date} {creation-time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters)</value>\r
   </data>\r
   <data name="Options_DefaultPathAdditionalParams" xml:space="preserve">\r
     <value>Available additional Options: {source_path} or {source_folder_name} \r