From: Vásáry Dániel Date: Fri, 4 Jan 2019 13:12:35 +0000 (+0000) Subject: #116 Multiszegmens adatok mentési logikája X-Git-Url: http://git.useribm.hu/?a=commitdiff_plain;h=5c4c1823c15244631f284c2ea748498005039489;p=mediacube.git #116 Multiszegmens adatok mentési logikája git-tfs-id: [http://tfs.userrendszerhaz.hu:8080/tfs/DefaultCollection]$/MediaCube;C31369 --- diff --git a/client/DxPlay/Configuration/dxplay.en b/client/DxPlay/Configuration/dxplay.en index 6e689af7..d73c2d89 100644 --- a/client/DxPlay/Configuration/dxplay.en +++ b/client/DxPlay/Configuration/dxplay.en @@ -4,7 +4,7 @@ "COMPLETED": "COMPLETED", "DEFINESEGMENT": "Define segment", "DELETESEGMENT": "Delete segment", - "ERRORTRAFFICCONNECT": "Could not connect to PlanAir system, using: {0}", + "ERRORTRAFFICCONNECT": "Error in PlanAir system communication. System message is: {0}", "EXITING": "EXITING", "LENGTH": "LENGTH", "METADATA": "Metadata", @@ -20,10 +20,11 @@ "PLAY": "Play (Space)", "PLAYING": "PLAYING", "STEPBACK": "Step back (<)", - "STEPFORWARD": "Step forward (>)", + "STEPFORWARD": "Step forward (>)", "POSITIONASTCIN": "Current position as TC in", "POSITIONASTCOUT": "Current position as TC out", "SEGMENTS": "Segments", + "SEGMENT": "Segment", "SPLITSEGMENT": "Split segment", "START": "START", "STOP": "Stop", @@ -46,9 +47,13 @@ "SEGMENTCOUNT": "Segment count", "TRAFFICID": "Traffic ID", "IDNOTEXIST": "Specified ID not exists.", - "ERRORCREATESEGMENT": "No space for mode segments.", + "ERRORCREATESEGMENT": "No space for more segments.", "ERRORINVALIDTCIN": "TC IN must be before TC OUT.", "ERRORINVALIDTCOUT": "TC OUT must be after TC IN.", "ERRORSEGMENTCOLLISION": "Segment overlaps are not allowed.", - "ERRORTARGETEXISTS": "Target file already exists: {0}" + "ERRORTARGETEXISTS": "Target file already exists: {0}", + "REDEFINESEGMENTS": "Redefine segments", + "REDEFINE": "Redefine", + "WRONGFILEFORREDEFINE": "The file name must be the same.", + "MEDIAID": "Media ID" } diff --git a/client/DxPlay/Configuration/dxplay.json b/client/DxPlay/Configuration/dxplay.json index c958879c..7718a798 100644 --- a/client/DxPlay/Configuration/dxplay.json +++ b/client/DxPlay/Configuration/dxplay.json @@ -1,8 +1,8 @@ { - "targetDirectory": "DONE", + "targetDirectory1": "c:/_video/DONE", "uiFileName": "dxplay.en", "isMaximized": false, - "isStandalone": false, + "isStandalone": true, "player": { "autoStart": false, "segmentEditor": true @@ -15,6 +15,8 @@ "userName": "MAM", "password": "7RKZYBzumKjL40SJwuwiFCvX57xuCN8zay6OttUm2wbrgImyYZBHyZTUUYrXX31Ge2Uwew07HYsqh2uzdJeDBDwcVntxaHg3nIpv9Dyq/odVoiC4tUF/K+lgvKWANcrZ", "timeout": 10000 - } + }, + "version": 0, + "redefineSegments": true } } diff --git a/client/DxPlay/DxPlayer.cs b/client/DxPlay/DxPlayer.cs index 294b412a..9e9bc46d 100644 --- a/client/DxPlay/DxPlayer.cs +++ b/client/DxPlay/DxPlayer.cs @@ -10,6 +10,7 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; @@ -38,6 +39,12 @@ namespace DxPlay { public Dictionary stringValues = new Dictionary(); public MediaDescription MediaDescription { get; internal set; } public Timecode CurrentTC { get; internal set; } + + public void SetState(GraphState state, [CallerLineNumber] int i = 0) { + State = state; + logger.Debug("State {0} ({1})", State, i); + } + public GraphState State { get; internal set; } private ManualResetEvent m_mre; private BackgroundWorker tcWorker; @@ -79,7 +86,7 @@ namespace DxPlay { stringValues.Add(GraphState.Playing, settings.Resource("PLAYING", Resources.PLAYING)); stringValues.Add(GraphState.Stopped, settings.Resource("STOPPED", Resources.STOPPED)); stringValues.Add(GraphState.Completed, settings.Resource("COMPLETED", Resources.COMPLETED)); - State = GraphState.Stopped; + SetState(GraphState.Stopped); try { int hr; MediaDescription = mediaDesc; @@ -174,18 +181,18 @@ namespace DxPlay { int hr = graph.MediaControl.Run(); DsError.ThrowExceptionForHR(hr); - State = GraphState.Playing; + SetState(GraphState.Playing); } } // Pause the capture graph. public void Pause() { // If we are playing - if (State == GraphState.Playing) { + if (State == GraphState.Playing || State == GraphState.Completed) { int hr = graph.MediaControl.Pause(); DsError.ThrowExceptionForHR(hr); - State = GraphState.Paused; + SetState(GraphState.Paused); Seek(CurrentTC.ZeroBasedFrames); } } @@ -193,29 +200,29 @@ namespace DxPlay { // Pause the capture graph. public void Stop() { //// Can only Stop when playing or paused - Seek(0); - Pause(); - if (State == GraphState.Playing || State == GraphState.Paused) { + if (State == GraphState.Playing || State == GraphState.Paused || State == GraphState.Completed) { + Seek(0); + Pause(); int hr = graph.MediaControl.StopWhenReady(); DsError.ThrowExceptionForHR(hr); - State = GraphState.Stopped; + SetState(GraphState.Stopped); } } public void Rewind(int step) { if (CurrentTC.ZeroBasedFrames > step - 1) { - Pause(); Seek(CurrentTC.ZeroBasedFrames - step); } } public void Forward(int step) { if (CurrentTC.ZeroBasedFrames + step <= MediaDescription.Duration.Frames) { - Pause(); Seek(CurrentTC.ZeroBasedFrames + step); } } - public void Seek(int value) { + public void Seek(int value, bool noPause = false) { + if (!noPause) + Pause(); SeekTo = value; } @@ -242,8 +249,9 @@ namespace DxPlay { corrected = true; } - if (State == GraphState.Completed) - State = GraphState.Stopped; + //if (State == GraphState.Completed) + // State = GraphState.Stopped; + //ForceRepaintFrame(); logger.Debug("Seeking requested frame {0} got frame {1}, media position {2}, frame length {3}, corrected {4}", value, reachedFrames, requestedPosition, avgTimePerFrame, corrected); } @@ -417,12 +425,11 @@ namespace DxPlay { if (isDisposed) return; logger.Debug("Dispose"); - GC.SuppressFinalize(this); if (tcWorker != null) tcWorker.CancelAsync(); + GC.SuppressFinalize(this); if (State != GraphState.Exiting) { - State = GraphState.Exiting; - + SetState(State = GraphState.Exiting); // Release the thread (if the thread was started) if (m_mre != null) { m_mre.Set(); @@ -436,8 +443,8 @@ namespace DxPlay { } GC.Collect(); isDisposed = true; - //if (m_eventThread != null) - // m_eventThread.Join(); + if (m_eventThread != null) + m_eventThread.Join(); } public int SampleCB(double SampleTime, IMediaSample pSample) { @@ -515,7 +522,7 @@ namespace DxPlay { // If the clip is finished playing if (ec == EventCode.Complete) { - State = GraphState.Completed; + SetState(GraphState.Completed); } // Release any resources the message allocated diff --git a/client/DxPlay/Integration/Traffic.cs b/client/DxPlay/Integration/Traffic.cs index 372715f5..cb06396f 100644 --- a/client/DxPlay/Integration/Traffic.cs +++ b/client/DxPlay/Integration/Traffic.cs @@ -22,12 +22,21 @@ namespace DxPlay.Integration { case MetadataType.TrafficMaterial: selector.LookupByMaterialID(id); break; + case MetadataType.TrafficMaterialSegment: + if (selector.MultiSegment) + selector.LookupByMaterialID(id); + else + selector.ClearLookup(); + break; case MetadataType.TrafficPromo: selector.LookupByPromoID(id); break; case MetadataType.TrafficAD: selector.LookupByADID(id); break; + default: + selector.ClearLookup(); + break; } } @@ -36,7 +45,7 @@ namespace DxPlay.Integration { switch (metadataType) { case MetadataType.TrafficMaterial: { - var trafficItem = selector.trafficAPI.GetMaterials(id, false).FirstOrDefault(); + var trafficItem = selector.trafficAPI.GetMaterials(id, false)?.FirstOrDefault(); if (trafficItem != null) result = selector.trafficAPI.GetMaterialSegments(trafficItem.VariantID); break; @@ -53,16 +62,22 @@ namespace DxPlay.Integration { return result; } - public void SaveSegments(TargetUpdateTrafficMessage message) { + public string SaveSegments(TargetUpdateTrafficMessage message) { //logger.Info("Now update!!!!!!!!!"); //TODO a listabol kijelolt szegmenseket is at kell adni - selector.trafficAPI.SaveSegments(message.VariantID, message.MetadataType, message.Segments, null); + return selector.trafficAPI.SaveSegments(message.VariantID, message.MetadataType, message.Segments, message.Selected); //message.Ready, } public void Approve(TargetUpdateTrafficMessage message) { - //TODO ifCompleted - selector.trafficAPI.Approve(message.VariantID, message.Ready, message.MetadataType); + TrafficItem trafficItem = null; + if (message.Segments?.Count == 0) { + logger.Info("Can't approve, no segments defined"); + return; + } + if (message.Selected != null && message.Selected.Count > 0) + trafficItem = message.Selected[0]; + selector.trafficAPI.Approve(message.VariantID, message.Ready, message.MetadataType, trafficItem, selector.MultiSegment); } } } diff --git a/client/DxPlay/Model/DxPlayModel.cs b/client/DxPlay/Model/DxPlayModel.cs index 2a154a29..e976b95d 100644 --- a/client/DxPlay/Model/DxPlayModel.cs +++ b/client/DxPlay/Model/DxPlayModel.cs @@ -22,6 +22,7 @@ namespace DxPlay.Model { private readonly DxPlaySettings settings; private Traffic traffic; + private bool multiSegment; public void InitializeTrafficIntegration(TrafficIDSelector selector) { traffic = new Traffic(selector, MessageBus); @@ -36,14 +37,16 @@ namespace DxPlay.Model { IsSegmentEditorVisible = true.Equals(settings?.Player?.SegmentEditor); IsMenuVisible = true.Equals(settings?.IsStandalone); Segments = settings.Segments ?? new BindingList(); - messageBus.Subscribe(OnMessage); + multiSegment = true.Equals(settings?.Metadata?.MultiSegmentEnabled); + MessageBus.Subscribe(OnMessage); + } private void OnMessage(IMessage message) { if (message is TrafficAPIMessage) { TrafficAPIMessage msg = message as TrafficAPIMessage; - string errorMessage = string.Format(settings.Resource("ERRORTRAFFICCONNECT", Resources.ERRORTRAFFICCONNECT), settings?.Metadata?.Server?.Address?.OriginalString); + string errorMessage = string.Format(settings.Resource("ERRORTRAFFICCONNECT", Resources.ERRORTRAFFICCONNECT), msg.Exception.Message); logger.Error(errorMessage); MsgBox.Error(errorMessage); //throw new Exception(msg.Content); @@ -55,9 +58,9 @@ namespace DxPlay.Model { logger.Info(propertyName); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + public IMessageBus MessageBus { get; } = new MessageBus(); - private IMessageBus messageBus = new MessageBus(); - public IMessageBus MessageBus { get => messageBus; } + public string RedefineSegmentMediaID { get; set; } private bool isSegmentEditorVisible; public bool IsSegmentEditorVisible { @@ -72,6 +75,9 @@ namespace DxPlay.Model { } } + + public bool IsRedefineSegments { get; set; } + private bool isMenuVisible; public bool IsMenuVisible { get { @@ -87,7 +93,13 @@ namespace DxPlay.Model { public bool IsApproveEnabled { get { - return id != null && id.Length != 0 && CurrentFile != null && CurrentFile.Exists; + bool enabled = id != null && id.Length != 0 && CurrentFile != null && CurrentFile.Exists; + + if (IsRedefineSegments && SelectedSegments != null && Segments != null) { + enabled &= SelectedSegments.Count == Segments.Count; + enabled &= Segments.Count(s => s.MediaID == RedefineSegmentMediaID) > 0; + } + return enabled; } } @@ -95,17 +107,16 @@ namespace DxPlay.Model { public string ID { get { return id; } set { - if (value != id) { - id = value; - Notify(); - Notify("IsApproveEnabled"); - MetadataType = MetadataTypeUtil.Guess(id); - InitializeSegments(); - } + id = value; + Notify(); + MetadataType = MetadataTypeUtil.Guess(id); + InitializeSegments(); + Notify("IsApproveEnabled"); } } public int VariantID { get; set; } + public List SelectedSegments { get; set; } public MetadataType MetadataType { get; set; } private FileInfo currentFile; @@ -116,19 +127,32 @@ namespace DxPlay.Model { if (currentFile != value) { currentFile = value; Notify(); - Notify("IsApproveEnabled"); } } } public BindingList Segments { get; private set; } - private void InitializeSegments() { + public void InitializeSegments() { Segments.Clear(); + if (id == null) + return; try { List segments = traffic.LoadSegments(id, MetadataType); - if (segments != null) - segments.ForEach(s => Segments.Add(s)); + if (segments != null) { + segments.ForEach(s => { + //ebben az esetben nem jon MediaID a szegmenssel + if (!multiSegment) + s.MediaID = ID; + + if (IsRedefineSegments) { + s.ReadOnly = RedefineSegmentMediaID != s.MediaID; + } else { + s.ReadOnly = !string.IsNullOrWhiteSpace(s.MediaID); + } + Segments.Add(s); + }); + } } catch (Exception) { throw; } @@ -143,34 +167,40 @@ namespace DxPlay.Model { return; string targetDirectory = settings.TargetDirectory; - string targetPath = Path.Combine(settings.TargetDirectory, ID + ".MXF"); - bool deleteTarget = false; + bool moveFile = !string.IsNullOrWhiteSpace(targetDirectory) && !IsRedefineSegments && Segments?.Count > 0; try { - if (File.Exists(targetPath)) - throw new Exception(String.Format(settings.Resource("ERRORTARGETEXISTS", Resources.ERRORTARGETEXISTS), targetPath)); - if (!Directory.Exists(targetDirectory)) - Directory.CreateDirectory(targetDirectory); - - deleteTarget = true; - FileSystem.CopyFile(CurrentFile.FullName, targetPath, UIOption.AllDialogs, UICancelOption.ThrowException); - TargetUpdateTrafficMessage message = new TargetUpdateTrafficMessage { VariantID = VariantID, Ready = true, MetadataType = MetadataType, - Segments = Segments.ToList() + Segments = Segments.ToList(), + Selected = SelectedSegments }; - traffic.SaveSegments(message); - traffic.Approve(message); - SafeDelete(currentFile.FullName); - CurrentFile = null; + string fileName = null; + if (multiSegment) { + fileName = traffic.SaveSegments(message); + } else { + fileName = ID; + traffic.SaveSegments(message); + } + if (moveFile) { + string targetPath = Path.Combine(settings.TargetDirectory, fileName + ".MXF"); + if (File.Exists(targetPath)) + throw new Exception(string.Format(settings.Resource("ERRORTARGETEXISTS", Resources.ERRORTARGETEXISTS), targetPath)); + if (!Directory.Exists(targetDirectory)) + Directory.CreateDirectory(targetDirectory); + FileSystem.CopyFile(CurrentFile.FullName, targetPath, UIOption.AllDialogs, UICancelOption.ThrowException); + } + + traffic.Approve(message); + if (moveFile) + SafeDelete(currentFile.FullName); + CurrentFile = null; } catch (Exception e) { logger.Error(e); - if (deleteTarget) - SafeDelete(targetPath); throw e; } } diff --git a/client/DxPlay/PlayerForm.Designer.cs b/client/DxPlay/PlayerForm.Designer.cs index fe8e5f25..dd35ccd7 100644 --- a/client/DxPlay/PlayerForm.Designer.cs +++ b/client/DxPlay/PlayerForm.Designer.cs @@ -59,6 +59,8 @@ namespace DxPlay { this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuOpenFile = new DxPlay.Controls.BindableToolStripMenuItem(); this.menuApprove = new DxPlay.Controls.BindableToolStripMenuItem(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.menuRedefineSegments = new DxPlay.Controls.BindableToolStripMenuItem(); this.playerControls = new DxPlay.Controls.PlayerControls(); ((System.ComponentModel.ISupportInitialize)(this.mainSplit)).BeginInit(); this.mainSplit.Panel1.SuspendLayout(); @@ -185,7 +187,7 @@ namespace DxPlay { this.tpSegments.Location = new System.Drawing.Point(4, 4); this.tpSegments.Name = "tpSegments"; this.tpSegments.Padding = new System.Windows.Forms.Padding(3); - this.tpSegments.Size = new System.Drawing.Size(192, 30); + this.tpSegments.Size = new System.Drawing.Size(263, 304); this.tpSegments.TabIndex = 1; this.tpSegments.Text = "Segments"; this.tpSegments.UseVisualStyleBackColor = true; @@ -204,7 +206,7 @@ namespace DxPlay { dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black; - dataGridViewCellStyle1.SelectionBackColor = System.Drawing.Color.Gainsboro; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); dataGridViewCellStyle1.SelectionForeColor = System.Drawing.Color.Black; dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False; this.dgSegments.DefaultCellStyle = dataGridViewCellStyle1; @@ -215,12 +217,16 @@ namespace DxPlay { this.dgSegments.Name = "dgSegments"; this.dgSegments.RowHeadersVisible = false; this.dgSegments.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dgSegments.Size = new System.Drawing.Size(186, 0); + this.dgSegments.Size = new System.Drawing.Size(257, 273); this.dgSegments.TabIndex = 1; - this.dgSegments.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgSegments_CellContentClick); - this.dgSegments.CellMouseDoubleClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dgSegments_CellMouseDoubleClick); - this.dgSegments.ColumnAdded += new System.Windows.Forms.DataGridViewColumnEventHandler(this.dgSegments_ColumnAdded); - this.dgSegments.MouseClick += new System.Windows.Forms.MouseEventHandler(this.OnSegmentEditorMouseClick); + this.dgSegments.CellBeginEdit += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.OnSegmentsCellBeginEdit); + this.dgSegments.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.OnSegmentsCellContentClick); + this.dgSegments.CellMouseDoubleClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.OnSegmentsCellMouseDoubleClick); + this.dgSegments.CellMouseEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.OnSegmentsCellMouseEnter); + this.dgSegments.CellMouseLeave += new System.Windows.Forms.DataGridViewCellEventHandler(this.OnSegmentsCellMouseLeave); + this.dgSegments.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.OnSegmentsCellPainting); + this.dgSegments.ColumnAdded += new System.Windows.Forms.DataGridViewColumnEventHandler(this.OnSegmentsColumnAdded); + this.dgSegments.MouseClick += new System.Windows.Forms.MouseEventHandler(this.OnSegmentsMouseClick); // // segmentActions // @@ -237,7 +243,7 @@ namespace DxPlay { this.segmentActions.Location = new System.Drawing.Point(3, 3); this.segmentActions.Name = "segmentActions"; this.segmentActions.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; - this.segmentActions.Size = new System.Drawing.Size(186, 25); + this.segmentActions.Size = new System.Drawing.Size(257, 25); this.segmentActions.TabIndex = 0; this.segmentActions.Text = "toolStrip1"; // @@ -350,6 +356,7 @@ namespace DxPlay { // btnApprove // this.btnApprove.AutoSize = true; + this.btnApprove.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.btnApprove.BackColor = System.Drawing.Color.White; this.btnApprove.FlatAppearance.BorderSize = 0; this.btnApprove.FlatStyle = System.Windows.Forms.FlatStyle.Flat; @@ -404,7 +411,9 @@ namespace DxPlay { // this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.menuOpenFile, - this.menuApprove}); + this.menuApprove, + this.toolStripSeparator3, + this.menuRedefineSegments}); this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); this.fileToolStripMenuItem.Text = "File"; @@ -413,7 +422,7 @@ namespace DxPlay { // this.menuOpenFile.Name = "menuOpenFile"; this.menuOpenFile.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.menuOpenFile.Size = new System.Drawing.Size(165, 22); + this.menuOpenFile.Size = new System.Drawing.Size(215, 22); this.menuOpenFile.Text = "Open file"; this.menuOpenFile.Click += new System.EventHandler(this.menuOpenFile_Click); // @@ -421,10 +430,23 @@ namespace DxPlay { // this.menuApprove.Name = "menuApprove"; this.menuApprove.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); - this.menuApprove.Size = new System.Drawing.Size(165, 22); + this.menuApprove.Size = new System.Drawing.Size(215, 22); this.menuApprove.Text = "Approve"; this.menuApprove.Click += new System.EventHandler(this.OnApprove); // + // toolStripSeparator3 + // + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(212, 6); + // + // menuRedefineSegments + // + this.menuRedefineSegments.Name = "menuRedefineSegments"; + this.menuRedefineSegments.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R))); + this.menuRedefineSegments.Size = new System.Drawing.Size(215, 22); + this.menuRedefineSegments.Text = "Redefine segments"; + this.menuRedefineSegments.Click += new System.EventHandler(this.OnRedefineSegments); + // // playerControls // this.playerControls.AutoSize = true; @@ -512,6 +534,8 @@ namespace DxPlay { private NoFocusCueButton btnApprove; private Label labelSelectedMetadata; private TextBox txtSelectedID; + private ToolStripSeparator toolStripSeparator3; + private DxPlay.Controls.BindableToolStripMenuItem menuRedefineSegments; } } diff --git a/client/DxPlay/PlayerForm.cs b/client/DxPlay/PlayerForm.cs index 9d989e0b..5686b936 100644 --- a/client/DxPlay/PlayerForm.cs +++ b/client/DxPlay/PlayerForm.cs @@ -13,13 +13,15 @@ using System.Windows.Forms; namespace DxPlay { public partial class PlayerForm : Form { + private const string TITLE = "MediaCube Player"; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - + private const string DEFAULT_FILE_FILTER = "MXF files (*.MXF)|*.MXF"; private DateTime lastClick = DateTime.Now; private volatile bool trackBarAtUser; private DxPlayer m_play = null; private MediaDescription m_mediaDescription = null; - private OpenFileDialog openFileDialog = new OpenFileDialog() { Filter = "All files (*.*)|*.*" }; + + private OpenFileDialog openFileDialog = new OpenFileDialog(); private ToolTip tooltips; private DxPlaySettings settings; @@ -61,6 +63,7 @@ namespace DxPlay { mainMenu.DataBindings.Add(new Binding("Visible", model, "IsMenuVisible", false, DataSourceUpdateMode.Never)); menuOpenFile.DataBindings.Add(new Binding("Enabled", model, "IsMenuVisible", false, DataSourceUpdateMode.Never)); menuApprove.DataBindings.Add(new Binding("Enabled", model, "IsApproveEnabled", false, DataSourceUpdateMode.Never)); + //menuRedefineSegments.DataBindings.Add(new Binding("Enabled", model, "IsRedefineSegmentsEnabled", false, DataSourceUpdateMode.Never)); if (model.IsMenuVisible) { btnApprove.DataBindings.Add(new Binding("Enabled", model, "IsApproveEnabled", false, DataSourceUpdateMode.Never)); @@ -86,8 +89,9 @@ namespace DxPlay { private void OnSelectedIDChanged(string ID, int variantID, string text, List selected) { //logger.Info("Selected ID: " + ID); try { - model.ID = ID; + model.SelectedSegments = selected; model.VariantID = variantID; + model.ID = ID; } catch (Exception e) { MsgBox.Error(e.Message); logger.Error(e); @@ -170,11 +174,7 @@ namespace DxPlay { } public void OpenFile(FileInfo fileInfo) { - logger.Debug("Open"); - if (model.IsMenuVisible) { - model.ID = null; - model.CurrentFile = null; - } + logger.Debug("Open {0}", fileInfo.FullName); if (m_play != null) m_play.Dispose(); @@ -185,8 +185,9 @@ namespace DxPlay { if (model.IsMenuVisible) { tabEditor.SelectedTab = tpMetadata; - trafficBrowser.ClearSelection(); + trafficBrowser.ClearLookup(); openFileDialog.InitialDirectory = fileInfo.Directory.FullName; + openFileDialog.Filter = DEFAULT_FILE_FILTER; string id = fileInfo.Name.Replace(fileInfo.Extension, ""); try { model.Lookup(id); @@ -195,6 +196,7 @@ namespace DxPlay { logger.Error(e); } } + model.IsRedefineSegments = false; //for (int i = 0; i < 100; i++) { @@ -250,7 +252,7 @@ namespace DxPlay { return; } if (m_play.State == GraphState.Completed) { - m_play.Seek(0); + m_play.Stop(); } m_play.Play(); UpdatePlayPauseButton(); @@ -416,7 +418,7 @@ namespace DxPlay { private bool HandleHotKey(Keys keyCode) { logger.Debug("Key pressed " + keyCode); if (!ApplicationIsActivated() || trafficBrowser.ContainsFocus || openFileDialogOpened || dgSegments.IsCurrentCellInEditMode || (m_play != null && !m_play.IsFullscreen() && !ContainsFocus)) - return true; + return false; logger.Debug("Handling " + keyCode); bool result = false; switch (keyCode) { @@ -531,9 +533,20 @@ namespace DxPlay { try { + m_play.Seek(0); m_play.Dispose(); + this.Text = TITLE; + playerControls.Status.Text = Settings.Resource("NOINPUT", Resources.NOINPUT); + playerControls.StartTC.Text = new Timecode().ToString(); + playerControls.CurrentTC.Text = new Timecode().ToString(); + playerControls.EndTC.Text = new Timecode().ToString(); + playerControls.TrackBar.Value = 0; + tabEditor.SelectedIndex = 0; model.Approve(); - trafficBrowser.ClearSelection(); + trafficBrowser.ClearLookup(); + btnApprove.Text = Settings.Resource("APPROVE", Resources.APPROVE); + model.IsRedefineSegments = false; + } catch (Exception ex) { logger.Error(ex); MsgBox.Error(ex.Message); @@ -541,6 +554,17 @@ namespace DxPlay { } + private void OnRedefineSegments(object sender, EventArgs e) { + openFileDialogOpened = true; + if (openFileDialog.ShowDialog() == DialogResult.OK) { + model.RedefineSegmentMediaID = Path.GetFileNameWithoutExtension(openFileDialog.FileName); + model.IsRedefineSegments = true; + OpenFile(new FileInfo(openFileDialog.FileName)); + } + openFileDialogOpened = false; + model.IsRedefineSegments = true; + btnApprove.Text = Settings.Resource("REDEFINE", Resources.REDEFINE); + } } } diff --git a/client/DxPlay/PlayerForm.resx b/client/DxPlay/PlayerForm.resx index 21ddb0c4..7a8247ef 100644 --- a/client/DxPlay/PlayerForm.resx +++ b/client/DxPlay/PlayerForm.resx @@ -173,7 +173,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABo - CAAAAk1TRnQBSQFMAgEBAgEAAaABAQGgAQEBGAEAARgBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + CAAAAk1TRnQBSQFMAgEBAgEAASABAgEgAQIBGAEAARgBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABYAMAARgDAAEBAQABCAYAAQkYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA diff --git a/client/DxPlay/PlayerFormSegments.cs b/client/DxPlay/PlayerFormSegments.cs index cd43b033..ef96d18f 100644 --- a/client/DxPlay/PlayerFormSegments.cs +++ b/client/DxPlay/PlayerFormSegments.cs @@ -2,151 +2,210 @@ using MaestroShared.Commons; using MaestroShared.Metadata; using System; +using System.Collections.Generic; +using System.Drawing; using System.Linq; using System.Windows.Forms; namespace DxPlay { public partial class PlayerForm : Form { + private int AddSegment(int framesIn, int framesOut) { + var result = new MovieSegment() { + TCIn = new Timecode(framesIn), + TCOut = new Timecode(framesOut) + }; + + if (model.IsRedefineSegments) + result.MediaID = model.RedefineSegmentMediaID; + + model.Segments.Add(result); + return model.Segments.Count - 1; + } + + private int MovieLastFrame { + get => new Timecode(m_mediaDescription.FirstFrame, m_mediaDescription.Duration).Frames; + } + + private int MovieFirstFrame { + get => m_mediaDescription.FirstFrame.Frames; + } + + private int MovieCurrentFrame { + get => m_play.CurrentTC.Frames; + } + + private MovieSegment GetLastSegment() { + MovieSegment result = null; + if (model.IsRedefineSegments) + result = model.Segments.Where(s => s.MediaID == model.RedefineSegmentMediaID).LastOrDefault(); + else + result = model.Segments.Where(s => s.MediaID == null).LastOrDefault(); + return result; + } + + private MovieSegment GetSegmentOverlaps(MovieSegment currentSegment, MovieSegment newSegment) { + IEnumerable x = null; + x = model.Segments.Where(s => + s != currentSegment && + s.MediaID == newSegment.MediaID && + newSegment.Overlaps(s) + ); + return x.FirstOrDefault(); + } + + private MovieSegment GetSegmentAt(int frames) { + IEnumerable x = null; + if (model.IsRedefineSegments) + x = model.Segments.Where(s => + s.MediaID == model.RedefineSegmentMediaID && + s.Contains(frames) + ); + else + x = model.Segments.Where(s => + s.MediaID == null && + s.Contains(frames) + ); + return x.FirstOrDefault(); + } + + //ha nincs szegmens teljes terjedelmu szegmens hozzaadasa + //ha van már szegmens, akkor az utolsó után private void OnDefineOneSegmentClick(object sender, EventArgs e) { if (m_play == null) return; - MovieSegment segment = null; - if (model.Segments.Count == 0) { - segment = new MovieSegment() { - TCIn = new Timecode(m_mediaDescription.FirstFrame.Frames), - TCOut = new Timecode(m_mediaDescription.FirstFrame, m_mediaDescription.Duration) - }; - } else { - MovieSegment lastSegment = model.Segments[model.Segments.Count - 1]; - Timecode tcEnd = new Timecode(m_mediaDescription.FirstFrame, m_mediaDescription.Duration); - if (lastSegment.TCOut.Frames == tcEnd.Frames) { + MovieSegment lastSegment = GetLastSegment(); + if (lastSegment == null) + bsSegments.Position = AddSegment(MovieFirstFrame, MovieLastFrame); + else { + if (lastSegment.TCOut.Frames == MovieLastFrame) MsgBox.Error(Settings.Resource("ERRORCREATESEGMENT", Resources.ERRORCREATESEGMENT)); - return; - } - segment = new MovieSegment() { - TCIn = new Timecode(lastSegment.TCOut.Frames), - TCOut = tcEnd - }; + else + bsSegments.Position = AddSegment(lastSegment.TCOut.Frames + 1, MovieLastFrame); } - model.Segments.Add(segment); } + //torli a kijelolt szegmenst private void OnDeleteSegmentClick(object sender, EventArgs e) { - if (bsSegments.Current != null) - model.Segments.Remove(bsSegments.Current as MovieSegment); + if (!(bsSegments.Current is MovieSegment currentSegment)) + return; + if (currentSegment.ReadOnly) + return; + model.Segments.Remove(currentSegment); } + //a kijelolt szegmensen beallitja az aktualis poziciot beleponek, ha nincs utkozes private void SetActualPositionAsIn() { if (m_play == null) return; - MovieSegment currentSegment = bsSegments.Current as MovieSegment; - if (currentSegment == null || bsSegments.Count == 0) { - MovieSegment newSegment = new MovieSegment() { - TCIn = new Timecode(m_play.CurrentTC.Frames), - TCOut = new Timecode(m_play.MediaDescription.FirstFrame.Frames + m_play.MediaDescription.Duration.Frames) - }; - bsSegments.Position = bsSegments.Add(newSegment); - } else { - if (currentSegment.TCOut.Frames < m_play.CurrentTC.Frames) { - int pos = bsSegments.IndexOf(currentSegment); - if (pos == bsSegments.Count - 1) { - MovieSegment newSegment = new MovieSegment() { - TCIn = new Timecode(m_play.CurrentTC.Frames), - TCOut = new Timecode(m_play.MediaDescription.FirstFrame.Frames + m_play.MediaDescription.Duration.Frames) - }; - bsSegments.Position = bsSegments.Add(newSegment); - return; - } - MsgBox.Error(Settings.Resource("ERRORINVALIDTCIN", Resources.ERRORINVALIDTCIN)); - return; - } - //if (MessageBox.Show("Biztos felül akarja írni az belépőt?", "Belépő felülírása", MessageBoxButtons.YesNo) == DialogResult.No) - // return; - MovieSegment collisionSegment = model.Segments.Where(s => s.TCIn.Frames < m_play.CurrentTC.Frames && m_play.CurrentTC.Frames <= s.TCOut.Frames).SingleOrDefault(); - - if (collisionSegment != null && !currentSegment.Equals(collisionSegment)) { - MsgBox.Error(Settings.Resource("ERRORSEGMENTCOLLISION", Resources.ERRORSEGMENTCOLLISION)); - return; - } - - currentSegment.TCIn = new Timecode(m_play.CurrentTC.Frames); + if (!(bsSegments.Current is MovieSegment currentSegment)) + return; + if (currentSegment.ReadOnly) + return; + if (MovieCurrentFrame >= currentSegment.TCOut.Frames) { + MsgBox.Error(Settings.Resource("ERRORINVALIDTCIN", Resources.ERRORINVALIDTCIN)); + return; } + MovieSegment newSegment = new MovieSegment() { + TCIn = new Timecode(MovieCurrentFrame), + TCOut = currentSegment.TCOut + }; + MovieSegment collisionSegment = GetSegmentOverlaps(currentSegment, newSegment); + if (collisionSegment != null) { + MsgBox.Error(Settings.Resource("ERRORSEGMENTCOLLISION", Resources.ERRORSEGMENTCOLLISION)); + return; + } + currentSegment.TCIn = new Timecode(MovieCurrentFrame); } - + //a kijelolt szegmensen beallitja az aktualis poziciot beleponek, ha nincs utkozes private void SetActualPositionAsOut() { if (m_play == null) return; - MovieSegment currentSegment = bsSegments.Current as MovieSegment; - if (currentSegment == null || bsSegments.Count == 0) { - MovieSegment newSegment = new MovieSegment() { - TCIn = new Timecode(m_play.MediaDescription.FirstFrame.Frames), - TCOut = new Timecode(m_play.CurrentTC.Frames), - }; - bsSegments.Position = bsSegments.Add(newSegment); - } else { - if (currentSegment.TCIn.Frames >= m_play.CurrentTC.Frames) - MsgBox.Error(Settings.Resource("ERRORINVALIDTCOUT", Resources.ERRORINVALIDTCOUT)); - - MovieSegment collisionSegment = model.Segments.Where(s => s.TCIn.Frames < m_play.CurrentTC.Frames && m_play.CurrentTC.Frames <= s.TCOut.Frames).SingleOrDefault(); - - if (collisionSegment != null && !currentSegment.Equals(collisionSegment)) { - MsgBox.Error(Settings.Resource("ERRORSEGMENTCOLLISION", Resources.ERRORSEGMENTCOLLISION)); - return; - } - - //if (MessageBox.Show("Biztos felül akarja írni az kilépőt?", "Kilépő felülírása", MessageBoxButtons.YesNo) == DialogResult.No) - // return; - currentSegment.TCOut = new Timecode(m_play.CurrentTC.Frames); + if (!(bsSegments.Current is MovieSegment currentSegment)) + return; + if (currentSegment.ReadOnly) + return; + if (MovieCurrentFrame <= currentSegment.TCIn.Frames) { + MsgBox.Error(Settings.Resource("ERRORINVALIDTCOUT", Resources.ERRORINVALIDTCOUT)); + return; } - } - - private void OnActualPositionToTCInToolStripMenuItem1Click(object sender, EventArgs e) { - SetActualPositionAsIn(); - } - private void OnActualPositionToTCOutToolStripMenuItem1Click(object sender, EventArgs e) { - SetActualPositionAsOut(); + MovieSegment newSegment = new MovieSegment() { + TCIn = currentSegment.TCIn, + TCOut = new Timecode(MovieCurrentFrame) + }; + MovieSegment collisionSegment = GetSegmentOverlaps(currentSegment, newSegment); + if (collisionSegment != null) { + MsgBox.Error(Settings.Resource("ERRORSEGMENTCOLLISION", Resources.ERRORSEGMENTCOLLISION)); + return; + } + currentSegment.TCOut = new Timecode(MovieCurrentFrame); } + //az aktualis pozicioban talalhato szegmenst kettevagja private void OnSplitSegmentAtCurrentPositionClick(object sender, EventArgs e) { - MovieSegment currentSegment = model.Segments.Where(s => s.TCIn.Frames <= m_play.CurrentTC.Frames && s.TCOut.Frames >= m_play.CurrentTC.Frames).SingleOrDefault(); - if (currentSegment == null) + MovieSegment currentSegment = GetSegmentAt(MovieCurrentFrame); + if (currentSegment == null || currentSegment.ReadOnly) + return; + if (MovieCurrentFrame == currentSegment.TCOut.Frames) + return; + if (currentSegment.TCIn.Frames == MovieCurrentFrame - 1) return; int position = model.Segments.IndexOf(currentSegment); + + MovieSegment newSegment = new MovieSegment() { TCIn = new Timecode(currentSegment.TCIn.Frames), - TCOut = new Timecode(m_play.CurrentTC.Frames - 1) + TCOut = new Timecode(MovieCurrentFrame - 1), }; - currentSegment.TCIn = new Timecode(m_play.CurrentTC.Frames); + if (model.IsRedefineSegments) + newSegment.MediaID = currentSegment.MediaID; + model.Segments.Insert(position, newSegment); + + currentSegment.TCIn = new Timecode(MovieCurrentFrame); } - private void dgSegments_ColumnAdded(object sender, DataGridViewColumnEventArgs e) { - int index = e.Column.Index; - switch (index) { - case 0: + private void OnSegmentsColumnAdded(object sender, DataGridViewColumnEventArgs e) { + string name = e.Column.DataPropertyName; + switch (name) { + case "TCIn": e.Column.HeaderText = Settings.Resource("TCIN", Resources.TCIN); e.Column.ReadOnly = true; break; - case 1: + case "TCOut": e.Column.HeaderText = Settings.Resource("TCOUT", Resources.TCOUT); e.Column.ReadOnly = true; break; - case 2: + case "Optional": e.Column.HeaderText = Settings.Resource("OPTIONAL", Resources.OPTIONAL); break; - case 3: + case "Comment": e.Column.HeaderText = Settings.Resource("COMMENT", Resources.COMMENT); break; + case "MediaID": + e.Column.HeaderText = Settings.Resource("MEDIAID", Resources.MEDIAID); + e.Column.ReadOnly = true; + break; + default: + dgSegments.Columns.Remove(e.Column); + break; } } - private void dgSegments_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e) { - MovieSegment actualSegment = bsSegments.Current as MovieSegment; - if (actualSegment == null || m_play == null) + + private void OnActualPositionToTCInToolStripMenuItem1Click(object sender, EventArgs e) { + SetActualPositionAsIn(); + } + + private void OnActualPositionToTCOutToolStripMenuItem1Click(object sender, EventArgs e) { + SetActualPositionAsOut(); + } + + private void OnSegmentsCellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e) { + if (!(bsSegments.Current is MovieSegment actualSegment) || actualSegment.ReadOnly || m_play == null) return; if (e.ColumnIndex == 0) { m_play.Pause(); @@ -171,12 +230,27 @@ namespace DxPlay { } - private void dgSegments_CellContentClick(object sender, DataGridViewCellEventArgs e) { - if (e.ColumnIndex == 2) - dgSegments.EndEdit(); + private void OnSegmentsCellContentClick(object sender, DataGridViewCellEventArgs e) { + if (!(dgSegments.Rows[e.RowIndex].DataBoundItem is MovieSegment item)) + return; + if (e.ColumnIndex == 2) { + if (item.ReadOnly) + dgSegments.CancelEdit(); + else + dgSegments.EndEdit(); + } } - private void OnSegmentEditorMouseClick(object sender, MouseEventArgs e) { + private void OnSegmentsCellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { + if (!(dgSegments.Rows[e.RowIndex].DataBoundItem is MovieSegment item)) + return; + + if (e.ColumnIndex == 3 && item.ReadOnly) { + e.Cancel = true; + } + } + + private void OnSegmentsMouseClick(object sender, MouseEventArgs e) { var ht = dgSegments.HitTest(e.X, e.Y); if (ht.Type == DataGridViewHitTestType.None) { @@ -184,5 +258,34 @@ namespace DxPlay { } } + private void OnSegmentsCellPainting(object sender, DataGridViewCellPaintingEventArgs e) { + if (e.RowIndex < 0) + return; + if (!(dgSegments.Rows[e.RowIndex].DataBoundItem is MovieSegment item)) + return; + DataGridViewRow row = dgSegments.Rows[e.RowIndex]; + if (item.ReadOnly) { + row.DefaultCellStyle.SelectionForeColor = Color.DarkGray; + row.DefaultCellStyle.ForeColor = Color.DarkGray; + } else { + row.DefaultCellStyle = null; + } + } + + private void OnSegmentsCellMouseEnter(object sender, DataGridViewCellEventArgs e) { + if (IsValidCellAddress(e.RowIndex, e.ColumnIndex)) + dgSegments.Cursor = Cursors.Hand; + } + + private void OnSegmentsCellMouseLeave(object sender, DataGridViewCellEventArgs e) { + if (IsValidCellAddress(e.RowIndex, e.ColumnIndex)) + dgSegments.Cursor = Cursors.Default; + } + + private bool IsValidCellAddress(int rowIndex, int columnIndex) { + return rowIndex >= 0 && rowIndex < dgSegments.RowCount && + columnIndex >= 0 && columnIndex <= 2; + } + } } diff --git a/client/DxPlay/Properties/AssemblyInfo.cs b/client/DxPlay/Properties/AssemblyInfo.cs index 9e2e5171..3e914ce2 100644 --- a/client/DxPlay/Properties/AssemblyInfo.cs +++ b/client/DxPlay/Properties/AssemblyInfo.cs @@ -26,7 +26,7 @@ using System.Runtime.CompilerServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("2.0.8.6")] +[assembly: AssemblyVersion("2.0.8.7")] // // In order to sign your assembly you must specify a key to use. Refer to the @@ -56,5 +56,5 @@ using System.Runtime.CompilerServices; [assembly: AssemblyDelaySign(false)] [assembly: AssemblyKeyFile("")] [assembly: AssemblyKeyName("")] -[assembly: AssemblyFileVersion("2.0.8.6")] +[assembly: AssemblyFileVersion("2.0.8.7")] diff --git a/client/DxPlay/Properties/Resources.Designer.cs b/client/DxPlay/Properties/Resources.Designer.cs index 6ede38cf..430901f0 100644 --- a/client/DxPlay/Properties/Resources.Designer.cs +++ b/client/DxPlay/Properties/Resources.Designer.cs @@ -276,6 +276,15 @@ namespace DxPlay.Properties { } } + /// + /// Looks up a localized string similar to Média ID. + /// + internal static string MEDIAID { + get { + return ResourceManager.GetString("MEDIAID", resourceCulture); + } + } + /// /// Looks up a localized string similar to Metaadat. /// @@ -384,6 +393,24 @@ namespace DxPlay.Properties { } } + /// + /// Looks up a localized string similar to Újraszegmentálás. + /// + internal static string REDEFINE { + get { + return ResourceManager.GetString("REDEFINE", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Újraszegmentálás. + /// + internal static string REDEFINESEGMENTS { + get { + return ResourceManager.GetString("REDEFINESEGMENTS", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hátra csévélés (Ctrl + <). /// @@ -473,5 +500,14 @@ namespace DxPlay.Properties { return ResourceManager.GetString("TCOUT", resourceCulture); } } + + /// + /// Looks up a localized string similar to A fájl nevének egyeznie kell a korábban megadottal.. + /// + internal static string WRONGFILEFORREDEFINE { + get { + return ResourceManager.GetString("WRONGFILEFORREDEFINE", resourceCulture); + } + } } } diff --git a/client/DxPlay/Properties/Resources.resx b/client/DxPlay/Properties/Resources.resx index 258c9ba4..30684219 100644 --- a/client/DxPlay/Properties/Resources.resx +++ b/client/DxPlay/Properties/Resources.resx @@ -253,4 +253,16 @@ Előre léptetés (>) + + Újraszegmentálás + + + Újraszegmentálás + + + A fájl nevének egyeznie kell a korábban megadottal. + + + Média ID + \ No newline at end of file diff --git a/client/Maestro/Configuration/configuration - Copy.json b/client/Maestro/Configuration/configuration - Copy.json index 188c8a81..ad4c3fb1 100644 --- a/client/Maestro/Configuration/configuration - Copy.json +++ b/client/Maestro/Configuration/configuration - Copy.json @@ -1,6 +1,6 @@ { "title": "Development", - "active": false, + "active": true, "startInTray": false, "enableCustomMetadataId": true, "filter": "avi", @@ -10,7 +10,7 @@ "$type": "NEXIOSource", "hideEmpty": true, "local": { - "address": "ws://10.10.1.27:88/services/nexio" + "address": "ws://10.10.1.27/services/nexio" }, "remote": { "address": "ftp://10.10.1.55:2098", @@ -38,7 +38,7 @@ { "$type": "MediaCubeMetadata", "server": { - "address": "http://localhost:8888/services/rest/jobengine", + "address": "http://10.10.1.27/services/rest/jobengine", "userName": "mediacube", "password": "Dn8t4gfHcK98o8hyPgLDhr5SgSji4JCxsfpMJsODikUp3nXgrM0UNCi45lLAK8ZOnmEneO44P9qpJ4QDqhctN6MxZodjJgdZTyoZKmSa+ECzEzLr/wPYNgxVaXrVotEy", "timeout": 1000 @@ -52,9 +52,11 @@ "outputFormat": "%ID%-%SOURCENAME%", "tag": "Betöltés", "killDateDays": 7, + "sourceNexioAgency": "ARCHIVED1", + "sourceNexioKillDateDays": 1, "saveArchiveMetadata": false, "remote": { - "address": "ftp://10.10.1.100/TESZT", + "address": "ftp://10.10.1.105/TESZT", "userName": "mediacube", "password": "Dn8t4gfHcK98o8hyPgLDhr5SgSji4JCxsfpMJsODikUp3nXgrM0UNCi45lLAK8ZOnmEneO44P9qpJ4QDqhctN6MxZodjJgdZTyoZKmSa+ECzEzLr/wPYNgxVaXrVotEy", "timeout": 1000 diff --git a/client/Maestro/Configuration/configuration.json b/client/Maestro/Configuration/configuration.json index 3932bcbd..cf268ea6 100644 --- a/client/Maestro/Configuration/configuration.json +++ b/client/Maestro/Configuration/configuration.json @@ -30,7 +30,7 @@ "$type": "OctopusMetadata", "server": { "address": "http://10.10.1.27/services/rest/octopus", - "timeout": 3000 + "timeout": 5000 } }, { @@ -41,12 +41,13 @@ "password": "7RKZYBzumKjL40SJwuwiFCvX57xuCN8zay6OttUm2wbrgImyYZBHyZTUUYrXX31Ge2Uwew07HYsqh2uzdJeDBDwcVntxaHg3nIpv9Dyq/odVoiC4tUF/K+lgvKWANcrZ", "timeout": 1000 }, - "version": 1 + "version": 0, + "redefineSegments": true }, { "$type": "MediaCubeMetadata", "server": { - "address": "http://10.10.1.29:88/services/rest/jobengine", + "address": "http://10.10.1.27/services/rest/jobengine", "userName": "mediacube", "password": "Dn8t4gfHcK98o8hyPgLDhr5SgSji4JCxsfpMJsODikUp3nXgrM0UNCi45lLAK8ZOnmEneO44P9qpJ4QDqhctN6MxZodjJgdZTyoZKmSa+ECzEzLr/wPYNgxVaXrVotEy", "timeout": 1000 @@ -56,14 +57,31 @@ "timeout": 1000 }, "jobTemplate": "retrieve-material.xml", - "archiveFolder": "file://10.10.1.100/BRAAVOS/ARCHIVE", - "restoreFolder": "file://10.10.1.100/BRAAVOS/ARCHIVE_RESTORE", + "archiveFolder": "file://10.10.1.105/TESZT/TC/ARCHIVE", + "restoreFolder": "file://10.10.1.105/BRAAVOS/ARCHIVE_RESTORE", "restoreNamePattern": "%s_%GUID%", "serverRestoreFolder": "/mnt/ISILON/ARCHIVE_RESTORE", "killDateDays": 1 } ], "targets": [ + { + "label": "Stúdióba küldés", + "processor": "FTPTargetProcessor", + "outputFormat": "%ID%", + "tag": "TESZT", + "nexioServer": true, + "nexioFileExistsMessage": "A feltöltéshez új 'placeholder' generálása szükséges az Octopus rendszerben, az anyagon belül az ALT+1 billenytűkombináció segítségével.", + "killDateDays": 1, + "disableFileVersioning": true, + "agency": "ARCHIVED", + "remote": { + "address": "ftp://10.10.1.55:2098", + "userName": "administrator", + "password": "+QtkeQdCTiOvZOgK/kUND4pO4/D+//r7ZIyluwMMdiqMEgO8iJErAG10ooWhPfiljQeXrdeyMzo7gWEZtcWpNSomGeDIbdMyQwtpqmMo1VEM3A27ZfzigY09YD46ECRh", + "timeout": 1000 + } + }, { "label": "Adáskész", "processor": "FTPTargetProcessor", diff --git a/client/Maestro/MaestroForm.Metadata.cs b/client/Maestro/MaestroForm.Metadata.cs index 13f3cec2..57c5b76f 100644 --- a/client/Maestro/MaestroForm.Metadata.cs +++ b/client/Maestro/MaestroForm.Metadata.cs @@ -1,5 +1,6 @@ using Interfaces; using Maestro.Metadata; +using Maestro.Sources; using MaestroShared.Commons; using MaestroShared.Configuration; using MaestroShared.Metadata; @@ -20,7 +21,16 @@ namespace Maestro { private const string MXFEXT = ".MXF"; private MetadataInfo selectedMetadata; - private BindingList movieSegments; + BindingList movieSegments; + private BindingList MovieSegments { + get { + return movieSegments; + } + set { + movieSegments = value; + UpdateDefineSegmentsStatus(); + } + } private MediaCubeApi mediaCubeApi; private ArchiveMetadata archiveMetadata; private static MetadataType[] validTypes = { MetadataType.TrafficAD, MetadataType.TrafficMaterial, MetadataType.TrafficPromo }; @@ -32,7 +42,7 @@ namespace Maestro { } set { selectedMetadata = value; - movieSegments = null; + MovieSegments = null; textSelectedMetadata.Text = value?.ID; ttMetadata.SetToolTip(textSelectedMetadata, GetMetadataTypeTooltip(value?.Kind)); } @@ -43,6 +53,7 @@ namespace Maestro { set { archiveMetadata = value; btnEditMetadata.ToolTipText = archiveMetadata == null ? "Metaadat" : archiveMetadata.ToString(); + UpdateEditArchiveMetadataStatus(); } } @@ -53,6 +64,7 @@ namespace Maestro { textSelectedMetadata.ReadOnly = !Configuration.EnableCustomMetadataId; UpdateDefineSegmentEnabled(); UpdateEditArchiveMetadataEnabled(); + } private void InitializeTrafficSelector() { @@ -115,7 +127,6 @@ namespace Maestro { ArchiveMetadata = saved; SelectedMetadata.MetadataText = ArchiveMetadata.itemTitle; } - UpdateEditArchiveMetadataStatus(); } private void OnDefineSegments(object sender, EventArgs e) { @@ -124,12 +135,31 @@ namespace Maestro { DefineSegments(SelectedSource.FileInfo, false); } + private void UpdateSegmentsAccessibility() { + if (MovieSegments == null) + return; + foreach (var s in MovieSegments) { + //eredetileg a szegmens adatokkal ez nem jon le singlesegment eseten, de kell a mukodeshez + if (!trafficIDSelector.MultiSegment && s.MediaID == null) + s.MediaID = SelectedMetadata.ID; + //redefine + if (true.Equals(SelectedMetadata?.IsRedefine)) + s.ReadOnly = SelectedMetadata.ID != s.MediaID; + else + s.ReadOnly = !string.IsNullOrWhiteSpace(s.MediaID); + } + } + private void DefineSegments(FileInfo fileInfo, bool redefine) { Cursor = Cursors.WaitCursor; - if (!redefine && (movieSegments == null || movieSegments.Count == 0)) - movieSegments = QuerySegments(); - if (movieSegments == null) - movieSegments = new BindingList(); + if (MovieSegments == null || MovieSegments.Count == 0) + MovieSegments = QuerySegments(); + + //redifine v nem + UpdateSegmentsAccessibility(); + + if (MovieSegments == null) + MovieSegments = new BindingList(); bool readOnly = true; if (ArchiveMetadata == null) { ArchiveMetadata = GetArchiveMetadata(); @@ -139,18 +169,16 @@ namespace Maestro { readOnly = ArchiveMetadata.ok; } else readOnly = ArchiveMetadata.ok; - if (readOnly && ArchiveMetadata != null) + if (readOnly && ArchiveMetadata != null && !SelectedMetadata.IsRedefine) MsgBox.Warning("Az anyag már el van fogadva, a szegmens módosítás nem kerül mentésre. A szegmensek módosításához vissza kell vonni az anyag elfogadását az adástervező rendszerben."); OpenFile(fileInfo, true, readOnly); - UpdateDefineSegmentsStatus(); Cursor.Current = Cursors.Default; } private BindingList QuerySegments() { BindingList result = null; List storedSegments = null; - TrafficMetadata metadata = MetadataProvider.Get(Configuration.Metadatas); - if (true.Equals(metadata?.MultiSegmentEnabled)) { + if (trafficIDSelector.MultiSegment) { storedSegments = new List(); foreach (TrafficItem item in SelectedMetadata.Selected) { if (item.MovieSegment != null) @@ -159,13 +187,14 @@ namespace Maestro { } else storedSegments = GetSegments(); - if (storedSegments != null && storedSegments.Count > 0) + if (storedSegments != null && storedSegments.Count > 0) { result = new BindingList(storedSegments); + } return result; } private void UpdateDefineSegmentsStatus() { - if (movieSegments == null || movieSegments.Count == 0) + if (MovieSegments == null || MovieSegments.Count == 0) btnDefineSegments.Image = Properties.Resources.ic_playlist_add_check_black_24dp_1x; else btnDefineSegments.Image = Properties.Resources.ic_playlist_add_check_black_24dp_1x_green; @@ -178,7 +207,6 @@ namespace Maestro { btnEditMetadata.Image = Properties.Resources.ic_receipt_black_24dp_1x_green; } - private List GetSegments() { List result = null; switch (selectedMetadata.Kind) { @@ -198,7 +226,6 @@ namespace Maestro { return result; } - private ArchiveMetadata GetArchiveMetadata() { ArchiveMetadata result = null; Cursor.Current = Cursors.WaitCursor; @@ -346,7 +373,7 @@ namespace Maestro { } } - movieSegments = null; + MovieSegments = null; ArchiveMetadata = null; UpdateProcessorButtonsEnabled(); @@ -387,15 +414,24 @@ namespace Maestro { VariantID = variantID, Selected = selected }; - - CheckIfRedefineSegments(); + TrafficMetadata metadata = MetadataProvider.Get(Configuration.Metadatas); + if (true.Equals(metadata?.RedefineSegments)) + CheckIfRedefineSegments(); } private void CheckIfRedefineSegments() { bool enableRedefine = false; string mediaHouseId = SelectedMetadata.ID + MXFEXT; + + if (bindingSource.DataSource is FileSystemSource source) { + string location = Path.Combine(source.Path, mediaHouseId); + enableRedefine = File.Exists(location); + if (enableRedefine) + SelectedMetadata.RedefineSegmentsFile = location; + } + MediaCubeMetadata metadata = MetadataProvider.Get(Configuration.Metadatas); - if (metadata != null && metadata.ArchiveFolder != null) { + if (!enableRedefine && metadata != null && metadata.ArchiveFolder != null) { string location = Path.Combine(metadata.ArchiveFolder.LocalPath, mediaHouseId); enableRedefine = File.Exists(location); if (enableRedefine) @@ -418,13 +454,16 @@ namespace Maestro { } } - if (enableRedefine) + if (enableRedefine && trafficIDSelector.trafficAPI != null) btnRedefineSegments.Visible = true; } private void OnRedefineSegments(object sender, EventArgs e) { if (SelectedMetadata == null) return; + + SelectedMetadata.IsRedefine = true; + MediaCubeMetadata mediaCubeMetadata = MetadataProvider.Get(Configuration.Metadatas); if (SelectedMetadata.RedefineWithRestore) { RestoreMedia restoreForm = new RestoreMedia(SelectedMetadata, mediaCubeMetadata, errorMessageBus); @@ -433,6 +472,12 @@ namespace Maestro { } DefineSegments(new FileInfo(SelectedMetadata.RedefineSegmentsFile), true); + SelectedMetadata.IsRedefine = false; + + if (MsgBox.YesNoQuestion("Biztosan elmenti a változtatásokat?")) { + trafficIDSelector.trafficAPI.SaveSegments(SelectedMetadata.VariantID, SelectedMetadata.Kind, MovieSegments?.ToList()); + trafficIDSelector.trafficAPI.Approve(SelectedMetadata.VariantID, MovieSegments?.Count > 0, SelectedMetadata.Kind); + } } private static string GetMetadataTypeTooltip(MetadataType? metadataType) { @@ -511,7 +556,6 @@ namespace Maestro { private void UpdateEditArchiveMetadataEnabled() { btnEditMetadata.Enabled = SelectedSource != null; - UpdateEditArchiveMetadataStatus(); } private void UpdateDefineSegmentEnabled() { @@ -522,7 +566,6 @@ namespace Maestro { SelectedSource.FileInfo != null && SelectedMetadata != null && validTypes.ToList().Contains(SelectedMetadata.Kind); - UpdateDefineSegmentsStatus(); } private void UpdateLookupByMetadataEnabled() { diff --git a/client/Maestro/MaestroForm.Source.cs b/client/Maestro/MaestroForm.Source.cs index dcd5971d..f178c58f 100644 --- a/client/Maestro/MaestroForm.Source.cs +++ b/client/Maestro/MaestroForm.Source.cs @@ -34,7 +34,7 @@ namespace Maestro { selectedSourceItems.Clear(); } textSelectedSource.Text = selectedSource?.Name; - movieSegments = null; + MovieSegments = null; UpdateProcessorButtonsEnabled(); UpdateDefineSegmentEnabled(); UpdateEditArchiveMetadataEnabled(); @@ -144,7 +144,7 @@ namespace Maestro { AutoStart = Configuration.Player.AutoStart, SegmentEditor = Configuration.Player.SegmentEditor && segmentEditor }, - Segments = movieSegments + Segments = MovieSegments }; player.OpenFile(fileInfo); if (!player.IsDisposed) diff --git a/client/Maestro/MaestroForm.Target.cs b/client/Maestro/MaestroForm.Target.cs index 0a54f414..7641a9ea 100644 --- a/client/Maestro/MaestroForm.Target.cs +++ b/client/Maestro/MaestroForm.Target.cs @@ -123,26 +123,27 @@ namespace Maestro { private bool EnsureSegments(Target target) { - if ((target.SaveMorpheusMetadata || target.SaveSegments) && (movieSegments == null || movieSegments.Count == 0)) { + if ((target.SaveMorpheusMetadata || target.SaveSegments) && (MovieSegments == null || MovieSegments.Count == 0)) { if (selectedMetadata?.VariantID != null && selectedMetadata?.VariantID != 0) { List storedSegments = GetSegments(); - if (storedSegments != null && storedSegments.Count > 0) - movieSegments = new BindingList(storedSegments); + if (storedSegments != null && storedSegments.Count > 0) { + MovieSegments = new BindingList(storedSegments); + } } - if (movieSegments == null) { + if (MovieSegments == null) { MessageBox.Show(String.Format("A {0} folyamat nem futtatható szegmens adatok nélkül.", target.Label)); return false; } switch (selectedMetadata.Kind) { case MetadataType.TrafficPromo: { - if (movieSegments.Count != 1) { + if (MovieSegments.Count != 1) { MessageBox.Show(String.Format("Promó anyagnak csak egy szegmens adata lehet.")); return false; } break; } case MetadataType.TrafficAD: { - if (movieSegments.Count != 1) { + if (MovieSegments.Count != 1) { MessageBox.Show(String.Format("Reklám anyagnak csak egy szegmens adata lehet.")); return false; } @@ -164,7 +165,6 @@ namespace Maestro { ArchiveMetadata = GetArchiveMetadata(); if (SelectedMetadata != null && String.IsNullOrEmpty(SelectedMetadata.MetadataText)) SelectedMetadata.MetadataText = ArchiveMetadata.itemTitle; - UpdateEditArchiveMetadataStatus(); if (ArchiveMetadata == null) { MessageBox.Show(String.Format("A {0} folyamat nem futtatható kísérő adatok nélkül.", target.Label)); return false; @@ -186,7 +186,7 @@ namespace Maestro { result.ID = textSelectedMetadata.Text; result.MetadataText = SelectedMetadata?.MetadataText; result.InputFileName = sourceItem.Name; - result.MovieSegments = movieSegments?.ToList(); + result.MovieSegments = MovieSegments?.ToList(); result.ArchiveMetadata = ArchiveMetadata.DeepClone(ArchiveMetadata); result.VariantID = selectedMetadata == null ? 0 : selectedMetadata.VariantID; result.MetadataKind = selectedMetadata == null ? MetadataType.None : selectedMetadata.Kind; diff --git a/client/Maestro/Metadata/MetaDataInfo.cs b/client/Maestro/Metadata/MetaDataInfo.cs index 4d1e4e85..dec05161 100644 --- a/client/Maestro/Metadata/MetaDataInfo.cs +++ b/client/Maestro/Metadata/MetaDataInfo.cs @@ -11,6 +11,7 @@ namespace Maestro.Metadata { public string RedefineSegmentsFile { get; set; } public bool RedefineWithRestore { get; set; } public Media RedefineMedia { get; set; } + public bool IsRedefine { get; set; } public List Selected { get; set; } } } diff --git a/client/Maestro/Properties/AssemblyInfo.cs b/client/Maestro/Properties/AssemblyInfo.cs index 882bf3bb..5eae53d8 100644 --- a/client/Maestro/Properties/AssemblyInfo.cs +++ b/client/Maestro/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.8.8")] -[assembly: AssemblyFileVersion("2.0.8.8")] +[assembly: AssemblyVersion("2.0.9.0")] +[assembly: AssemblyFileVersion("2.0.9.0")] diff --git a/client/MaestroShared/Configuration/ConfigurationInfo.cs b/client/MaestroShared/Configuration/ConfigurationInfo.cs index 5c8cc2a8..320d33cb 100644 --- a/client/MaestroShared/Configuration/ConfigurationInfo.cs +++ b/client/MaestroShared/Configuration/ConfigurationInfo.cs @@ -122,6 +122,8 @@ namespace MaestroShared.Configuration { public string NexioFileExistsMessage { get; set; } public string Agency { get; set; } public string PopupMessage { get; set; } + public string SourceNexioAgency { get; set; } + public int SourceNexioKillDateDays { get; set; } } public class Connection { @@ -191,6 +193,7 @@ namespace MaestroShared.Configuration { public ProjectSettings ProjectSettings { get; set; } public string FunctionName { get; set; } public int Version { get; set; } + public bool RedefineSegments { get; set; } public bool MultiSegmentEnabled { get => Version == 1; } diff --git a/client/MaestroShared/Interfaces/ITrafficAPI.cs b/client/MaestroShared/Interfaces/ITrafficAPI.cs index 7b9fd286..28bd8fd4 100644 --- a/client/MaestroShared/Interfaces/ITrafficAPI.cs +++ b/client/MaestroShared/Interfaces/ITrafficAPI.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Interfaces { public interface ITrafficAPI { - void Approve(int itemID, bool ready, MetadataType kind, bool ifCompleted = false); + void Approve(int itemID, bool ready, MetadataType kind, TrafficItem trafficItem = null, bool ifCompleted = false); TrafficVersion CreateMaterialVersion(string episodeID, bool recut); List GetADArchiveMetadata(string strParam); List GetADs(string search, bool problematic, DateTime? from = null, DateTime? to = null); @@ -15,6 +15,6 @@ namespace Interfaces { List GetPromoArchiveMetadata(string strParam); List GetPromos(string search, bool problematic, DateTime? from = null, DateTime? to = null); List GetPromoSegments(string strParam); - string SaveSegments(int itemID, MetadataType kind, List segments, List selectedSegments); + string SaveSegments(int itemID, MetadataType kind, List segments, List selectedSegments = null); } } \ No newline at end of file diff --git a/client/MaestroShared/Metadata/MetadataType.cs b/client/MaestroShared/Metadata/MetadataType.cs index 42627eb1..2f35ebcd 100644 --- a/client/MaestroShared/Metadata/MetadataType.cs +++ b/client/MaestroShared/Metadata/MetadataType.cs @@ -1,13 +1,14 @@ -using System; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; namespace MaestroShared.Metadata { public enum MetadataType { - None, MediaCube, OctopusStory, OctopusPlaceHolder, TrafficMaterial, TrafficPromo, TrafficAD + None, MediaCube, OctopusStory, OctopusPlaceHolder, TrafficMaterial, TrafficMaterialSegment, TrafficPromo, TrafficAD } public class MetadataTypeUtil { private const string REGEXP_TRAFFICMATERIALID = "^M{1}[0-9]{6}[A-Z]{1}$"; + private const string REGEXP_TRAFFICMATERIALID_V1 = "^M{1}[0-9]{6}[A-Z]{1}[0-9]{1}$"; + private const string REGEXP_TRAFFICMATERIALID_V2 = "^M{1}[0-9]{6}[A-Z]{1}[A-Z]{1}$"; private const string REGEXP_TRAFFICADID = "^R{1}[0-9]{6}[A-Z]{1}$"; private const string REGEXP_TRAFFICALTERNATEADID = "^C{1}[0-9]{6}[A-Z]{1}$"; private const string REGEXP_TRAFFICPROMOID = "^P{1}[0-9]{6}[A-Z]{1}$"; @@ -15,8 +16,9 @@ namespace MaestroShared.Metadata { private const string REGEXP_OCTOPUSPLACEHOLDERID = "^[0-9]+_[0-9]+$"; private const string REGEXP_OCTOPUSPLACEHOLDERVERSIONEDID = "^[0-9]+_[0-9]+-[0-9]{3}$"; + public static MetadataType Guess(string id) { - if (String.IsNullOrEmpty(id)) + if (string.IsNullOrEmpty(id)) return MetadataType.None; Match match = null; @@ -24,6 +26,14 @@ namespace MaestroShared.Metadata { if (match.Success) return MetadataType.TrafficMaterial; + match = Regex.Match(id, REGEXP_TRAFFICMATERIALID_V1); + if (match.Success) + return MetadataType.TrafficMaterialSegment; + + match = Regex.Match(id, REGEXP_TRAFFICMATERIALID_V2); + if (match.Success) + return MetadataType.TrafficMaterialSegment; + match = Regex.Match(id, REGEXP_TRAFFICPROMOID); if (match.Success) return MetadataType.TrafficPromo; diff --git a/client/MaestroShared/Metadata/MovieSegment.cs b/client/MaestroShared/Metadata/MovieSegment.cs index 0904fcb7..0d36f60a 100644 --- a/client/MaestroShared/Metadata/MovieSegment.cs +++ b/client/MaestroShared/Metadata/MovieSegment.cs @@ -8,10 +8,14 @@ namespace MaestroShared.Metadata { protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + public int SegNr; private Timecode tcIn; private Timecode tcOut; private bool optional; private string comment; + private string mediaID; + public int SegID; + public bool ReadOnly; public Timecode TCIn { get => tcIn; @@ -45,6 +49,20 @@ namespace MaestroShared.Metadata { } } + public string MediaID { + get => mediaID; + set { + mediaID = value; + NotifyPropertyChanged(); + } + } + public bool Contains(int frame) { + return TCIn.Frames <= frame && TCOut.Frames >= frame; + } + + public bool Overlaps(MovieSegment segment) { + return Contains(segment.TCIn.Frames) || Contains(segment.TCOut.Frames); + } } } diff --git a/client/MaestroShared/Metadata/Traffic.cs b/client/MaestroShared/Metadata/Traffic.cs index 66b69514..02df4a7c 100644 --- a/client/MaestroShared/Metadata/Traffic.cs +++ b/client/MaestroShared/Metadata/Traffic.cs @@ -19,7 +19,7 @@ namespace MaestroShared.Metadata { public string Segment { get { if (SegmentCount != null && SegmentNr != null) - return string.Format($"{SegmentCount} / {SegmentNr}"); + return string.Format($"{SegmentCount} / {SegmentNr} : {MovieSegment?.MediaID}"); else return ""; } diff --git a/client/MaestroShared/Properties/AssemblyInfo.cs b/client/MaestroShared/Properties/AssemblyInfo.cs index e4191e46..c05a92a6 100644 --- a/client/MaestroShared/Properties/AssemblyInfo.cs +++ b/client/MaestroShared/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.9.0")] -[assembly: AssemblyFileVersion("2.0.9.0")] +[assembly: AssemblyVersion("2.0.9.1")] +[assembly: AssemblyFileVersion("2.0.9.1")] diff --git a/client/MaestroShared/Targets/FXPTargetProcessor.cs b/client/MaestroShared/Targets/FXPTargetProcessor.cs index e8d68f51..c88dbb6b 100644 --- a/client/MaestroShared/Targets/FXPTargetProcessor.cs +++ b/client/MaestroShared/Targets/FXPTargetProcessor.cs @@ -1,10 +1,12 @@ using FluentFTP; +using MaestroShared.Commons; using MaestroShared.Configuration; using MaestroShared.Target; using NLog; using System; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; @@ -162,8 +164,45 @@ namespace MaestroShared.Targets { } } + private void UploadNexioSourceMetadata() { + logger.Trace(Strings.ENTRY); + string xml = null; + string name = Path.GetFileNameWithoutExtension(Input); + string description = Parameters.ArchiveMetadata?.mediaTitle; + if (Parameters.TargetConfig.KillDateDays > 0) { + DateTime date = GetKillDate(Parameters.TargetConfig.SourceNexioKillDateDays); + KillDate = date.ToString(DATE_FORMAT); + xml = NexioXML.ToXML(name, date, null, Parameters.TargetConfig.SourceNexioAgency); + } else + xml = NexioXML.ToXML(name, null, null, Parameters.TargetConfig.SourceNexioAgency); + byte[] content = Encoding.Unicode.GetBytes(xml); + UploadContentToSource(name + XML_EXT, content); + logger.Trace(Strings.EXIT); + } + + protected void UploadContentToSource(string outputPath, byte[] content) { + using (Stream ostream = sourceFTP.OpenWrite(outputPath, FtpDataType.Binary, false)) { + try { + ostream.Write(content, 0, content.Length); + } catch (Exception e) { + logger.Error(e.Message); + throw e; + } finally { + ostream.Close(); + } + } + FtpReply reply = sourceFTP.GetReply(); + if (!reply.Success || !_226.Equals(reply.Code)) { + throw new Exception(UPLOAD_ERROR); + } + } + protected override void AfterExecute() { base.AfterExecute(); + + if (isNexioSource && !string.IsNullOrWhiteSpace(Parameters.TargetConfig.SourceNexioAgency)) + UploadNexioSourceMetadata(); + TerminateClient(monitorFTP); TerminateClient(sourceFTP); } diff --git a/client/MaestroShared/Targets/TargetUpdateTrafficMessage.cs b/client/MaestroShared/Targets/TargetUpdateTrafficMessage.cs index e5fb23be..a1df5406 100644 --- a/client/MaestroShared/Targets/TargetUpdateTrafficMessage.cs +++ b/client/MaestroShared/Targets/TargetUpdateTrafficMessage.cs @@ -8,5 +8,6 @@ namespace MaestroShared.Targets { public bool Ready { get; set; } public MetadataType MetadataType { get; set; } public List Segments { get; set; } + public List Selected { get; set; } } } diff --git a/client/MaestroShared/Targets/UNCTargetProcessor.cs b/client/MaestroShared/Targets/UNCTargetProcessor.cs index 3b13eb76..bf3d6510 100644 --- a/client/MaestroShared/Targets/UNCTargetProcessor.cs +++ b/client/MaestroShared/Targets/UNCTargetProcessor.cs @@ -23,11 +23,11 @@ namespace MaestroShared.Targets { private const string KILLDATE_FILE = "{0}.{1}.killdate"; private const string METADATA_FILE = "{0}.json"; private const string NORMALIZE_TEXT_PATTERN = "[^0-9A-Za-z-._/]"; - private const string DATE_FORMAT = "yyyy.MM.dd"; + protected const string DATE_FORMAT = "yyyy.MM.dd"; private const string PROGRAMME = "PROGRAMME"; private const string COMMERCIAL = "COMMERCIAL"; private const string JUNCTION = "JUNCTION"; - private const string XML_EXT = ".xml"; + protected const string XML_EXT = ".xml"; private const string DOT = "."; private const string HYPHENSTAR = "-*"; private const string DATE_FORMAT_NODOTS = "yyyyMMdd"; @@ -74,7 +74,7 @@ namespace MaestroShared.Targets { string fileName = Parameters?.TrafficApi?.SaveSegments(Parameters.VariantID, Parameters.MetadataKind, Parameters.MovieSegments, Parameters.SelectedSegments); if (true.Equals(Parameters?.TrafficMetadata?.MultiSegmentEnabled)) { //a Traffic adja a nevet - OutputName = fileName; + OutputName = fileName ?? throw new Exception("A fájlnév nem lehet üres."); } } } @@ -208,7 +208,10 @@ namespace MaestroShared.Targets { if (Parameters.TargetConfig.SaveSegments && Parameters.MovieSegments != null) { bool ifCompleted = true.Equals(Parameters?.TrafficMetadata?.MultiSegmentEnabled); - Parameters?.TrafficApi?.Approve(Parameters.VariantID, true, Parameters.MetadataKind, ifCompleted); + TrafficItem trafficItem = null; + if (Parameters.SelectedSegments != null && Parameters.SelectedSegments.Count > 0) + trafficItem = Parameters.SelectedSegments[0]; + Parameters?.TrafficApi?.Approve(Parameters.VariantID, true, Parameters.MetadataKind, trafficItem, ifCompleted); } if (Parameters.TargetConfig.SendEmailOnSuccess && !String.IsNullOrEmpty(Parameters.TargetConfig.SuccessEmailRecipient) && !String.IsNullOrEmpty(Parameters.TargetConfig.SuccessEmailPattern)) SendEmail(Parameters.TargetConfig.SuccessEmailRecipient, Parameters.TargetConfig.SuccessEmailPattern); @@ -221,7 +224,7 @@ namespace MaestroShared.Targets { string name = GetOutputName(); string description = Parameters.ArchiveMetadata?.mediaTitle; if (Parameters.TargetConfig.KillDateDays > 0) { - DateTime date = GetKillDate(); + DateTime date = GetKillDate(Parameters.TargetConfig.KillDateDays); KillDate = date.ToString(DATE_FORMAT); xml = NexioXML.ToXML(name, date, description, Parameters.TargetConfig.Agency); } else @@ -295,7 +298,7 @@ namespace MaestroShared.Targets { Parameters?.MediaCubeApi?.Create(workFlowAction); } catch (Exception e) { logger.Error(e); - MessageBox.Show(parent, e.Message); + //MsgBox.Error(e.Message); } logger.Trace(Strings.EXIT); } @@ -339,10 +342,10 @@ namespace MaestroShared.Targets { return result; } - private DateTime GetKillDate() { + protected DateTime GetKillDate(int killDateDays) { logger.Trace(Strings.ENTRY); DateTime result = DateTime.Now; - result = result.AddDays(Parameters.TargetConfig.KillDateDays); + result = result.AddDays(killDateDays); logger.Trace(Strings.EXIT); return result; } @@ -352,7 +355,7 @@ namespace MaestroShared.Targets { Uri address = Parameters.TargetConfig.Remote.Address; string statusWorkDir = Path.Combine(workingDir, STATUS_FOLDER); EnsureDirectoryExistence(statusWorkDir); - DateTime date = GetKillDate(); + DateTime date = GetKillDate(Parameters.TargetConfig.KillDateDays); string fileName = String.Format(KILLDATE_FILE, OutputName, date.ToString(DATE_FORMAT_NODOTS)); //logger.Debug("Creating KILLDATE status file {0}", fileName); KillDatePath = GetOutputFilePath(statusWorkDir, fileName); diff --git a/client/MediaCubeClient/Properties/AssemblyInfo.cs b/client/MediaCubeClient/Properties/AssemblyInfo.cs index b08057c1..9b072e3b 100644 --- a/client/MediaCubeClient/Properties/AssemblyInfo.cs +++ b/client/MediaCubeClient/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.0.1")] +[assembly: AssemblyFileVersion("1.0.0.1")] diff --git a/client/MetadataSelector/Properties/AssemblyInfo.cs b/client/MetadataSelector/Properties/AssemblyInfo.cs index 9a8e1bb6..359bd8a1 100644 --- a/client/MetadataSelector/Properties/AssemblyInfo.cs +++ b/client/MetadataSelector/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.1")] -[assembly: AssemblyFileVersion("1.0.0.1")] +[assembly: AssemblyVersion("1.0.0.2")] +[assembly: AssemblyFileVersion("1.0.0.2")] diff --git a/client/PlanAIRClient/Model/PlanAirMaterialResult.cs b/client/PlanAIRClient/Model/PlanAirMaterialResult.cs index 11581b99..f6cf2565 100644 --- a/client/PlanAIRClient/Model/PlanAirMaterialResult.cs +++ b/client/PlanAIRClient/Model/PlanAirMaterialResult.cs @@ -25,6 +25,7 @@ public int? v_SegDuration; public string v_SegTitle; public string v_SegKeyWords; + public string v_SegMediaID; public bool? v_SegDropable; } } diff --git a/client/PlanAIRClient/Model/PlanAirMaterialSegmentResult.cs b/client/PlanAIRClient/Model/PlanAirMaterialSegmentResult.cs index 99615b9e..97fe41a8 100644 --- a/client/PlanAIRClient/Model/PlanAirMaterialSegmentResult.cs +++ b/client/PlanAIRClient/Model/PlanAirMaterialSegmentResult.cs @@ -1,190 +1,19 @@ namespace TrafficClient.Model { public partial class PlanAirMaterialSegmentResult { - private int _v_SegID; - - private System.Nullable _v_VariantID; - - private string _v_ClipID; - - private string _v_SegTitle; - - private string _v_SegKeyWords; - - private System.Nullable _v_SegNumber; - - private System.Nullable _v_TcIn; - - private System.Nullable _v_TcOut; - - private System.Nullable _v_Duration; - - private string _v_TcInTC; - - private string _v_TcOutTC; - - private string _v_DurationTC; - - private bool? _v_Dropable; - - public PlanAirMaterialSegmentResult() { - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_SegID", DbType = "Int NOT NULL")] - public int v_SegID { - get { - return this._v_SegID; - } - set { - if ((this._v_SegID != value)) { - this._v_SegID = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_VariantID", DbType = "Int")] - public System.Nullable v_VariantID { - get { - return this._v_VariantID; - } - set { - if ((this._v_VariantID != value)) { - this._v_VariantID = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_SegTitle", DbType = "VarChar", CanBeNull = false)] - public string v_SegTitle { - get { - return this._v_SegTitle; - } - set { - if ((this._v_SegTitle != value)) { - this._v_SegTitle = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_SegKeyWords", DbType = "Int")] - public string v_SegKeyWords { - get { - return this.v_SegKeyWords; - } - set { - if ((this._v_SegKeyWords != value)) { - this._v_SegKeyWords = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_SegNumber", DbType = "Int")] - public System.Nullable v_SegNumber { - get { - return this._v_SegNumber; - } - set { - if ((this._v_SegNumber != value)) { - this._v_SegNumber = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_TcIn", DbType = "int")] - public System.Nullable v_TcIn { - get { - return this._v_TcIn; - } - set { - if ((this._v_TcIn != value)) { - this._v_TcIn = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_TcOut", DbType = "Int")] - public System.Nullable v_TcOut { - get { - return this._v_TcOut; - } - set { - if ((this._v_TcOut != value)) { - this._v_TcOut = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_Duration", DbType = "Int")] - public System.Nullable v_Duration { - get { - return this._v_Duration; - } - set { - if ((this._v_Duration != value)) { - this._v_Duration = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_TcInTC", DbType = "VarChar(40)")] - public string v_TcInTC { - get { - return this._v_TcInTC; - } - set { - if ((this._v_TcInTC != value)) { - this._v_TcInTC = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_ClipID", DbType = "VarChar(8)")] - public string v_ClipID { - get { - return this._v_ClipID; - } - set { - if ((this._v_ClipID != value)) { - this._v_ClipID = value; - } - } - } - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_TcOutTC", DbType = "VarChar(25)")] - public string v_TcOutTC { - get { - return this._v_TcOutTC; - } - set { - if ((this._v_TcOutTC != value)) { - this._v_TcOutTC = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_DurationTC", DbType = "Varchar")] - public string v_DurationTC { - get { - return this._v_DurationTC; - } - set { - if ((this._v_DurationTC != value)) { - this._v_DurationTC = value; - } - } - } - - [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "v_Dropable", DbType = "bit")] - public bool? v_Dropable { - get { - return this._v_Dropable; - } - set { - if ((this._v_Dropable != value)) { - this._v_Dropable = value; - } - } - } - - + public int v_SegID; + public int? v_VariantID; + public string v_MediaID; + public string v_ClipID; + public string v_SegTitle; + public string v_SegKeyWords; + public int? v_SegNumber; + public int? v_TcIn; + public int? v_TcOut; + public int? v_Duration; + public string v_TcInTC; + public string v_TcOutTC; + public string v_DurationTC; + public bool? v_Dropable; } } diff --git a/client/PlanAIRClient/PlanAirDataReader.cs b/client/PlanAIRClient/PlanAirDataReader.cs index 1fc68f17..6b738c06 100644 --- a/client/PlanAIRClient/PlanAirDataReader.cs +++ b/client/PlanAIRClient/PlanAirDataReader.cs @@ -43,8 +43,6 @@ namespace TrafficClient { } public PlanAirMaterialResult ToSingleMaterialResult(SqlDataReader reader) { - //for (int i = 0; i < reader.FieldCount; i++) - // logger.Debug($"{i} : {reader.GetName(i)} {reader.GetDataTypeName(i)} {reader.GetFieldType(i)}"); PlanAirMaterialResult item = new PlanAirMaterialResult(); int f = 0; item.v_ProgrammeID = (int)ReadInt(reader, ref f); @@ -68,27 +66,9 @@ namespace TrafficClient { item.v_ForTransm = ReadBool(reader, ref f); return item; } - /* -USE [PA_Echo] -GO -DECLARE @return_value int - -EXEC @return_value = [dbo].[clIFsp_EC_MAM] - @Operation = 6002, - @@@Options = 0, - @@ItemID = NULL, - @@DateParam1 = N'2018-12-06', - @@DateParam2 = N'2018-12-06' - -SELECT 'Return Value' = @return_value - -GO - - */ public PlanAirMaterialResult ToMultiMaterialResult(SqlDataReader reader) { - //for (int i = 0; i < reader.FieldCount; i++) - // logger.Debug($"{i} : {reader.GetName(i)} {reader.GetDataTypeName(i)} {reader.GetFieldType(i)}"); + //reader.DumpColumns(); PlanAirMaterialResult item = new PlanAirMaterialResult(); int f = 0; item.v_ProgrammeID = (int)ReadInt(reader, ref f); @@ -114,6 +94,7 @@ GO item.v_SegDuration = ReadInt(reader, ref f); item.v_SegTitle = ReadString(reader, ref f); item.v_SegKeyWords = ReadString(reader, ref f); + item.v_SegMediaID = ReadString(reader, ref f); item.v_SegDropable = ReadBool(reader, ref f); item.v_FirstBroadcastDate = ReadDateTime(reader, ref f); item.v_NextBroadcastDate = ReadDateTime(reader, ref f); @@ -167,8 +148,10 @@ GO public PlanAirMaterialSegmentResult ToMaterialSegmentResult(SqlDataReader reader) { PlanAirMaterialSegmentResult item = new PlanAirMaterialSegmentResult(); int f = 0; + reader.DumpColumns(); item.v_SegID = (int)ReadInt(reader, ref f); item.v_VariantID = ReadInt(reader, ref f); + item.v_MediaID = ReadString(reader, ref f); item.v_SegTitle = ReadString(reader, ref f); item.v_SegKeyWords = ReadString(reader, ref f); item.v_SegNumber = ReadInt(reader, ref f); @@ -183,4 +166,14 @@ GO } } + + public static class SqlDataReaderExtension { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public static void DumpColumns(this SqlDataReader reader) { + for (int i = 0; i < reader.FieldCount; i++) + logger.Debug($"{i} : {reader.GetName(i)} {reader.GetDataTypeName(i)} {reader.GetFieldType(i)}"); + + } + } } diff --git a/client/PlanAIRClient/Properties/AssemblyInfo.cs b/client/PlanAIRClient/Properties/AssemblyInfo.cs index f8176cc3..f3791239 100644 --- a/client/PlanAIRClient/Properties/AssemblyInfo.cs +++ b/client/PlanAIRClient/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.1")] -[assembly: AssemblyFileVersion("1.0.0.1")] +[assembly: AssemblyVersion("1.0.0.2")] +[assembly: AssemblyFileVersion("1.0.0.2")] diff --git a/client/PlanAIRClient/TrafficAPI.cs b/client/PlanAIRClient/TrafficAPI.cs index d0d3b74b..998c2ba3 100644 --- a/client/PlanAIRClient/TrafficAPI.cs +++ b/client/PlanAIRClient/TrafficAPI.cs @@ -57,10 +57,10 @@ namespace TrafficClient { return materialWorker.GetMaterialArchiveMetadata(strParam); } - public void Approve(int itemID, bool ready, MetadataType kind, bool ifCompleted) { + public void Approve(int itemID, bool ready, MetadataType kind, TrafficItem trafficItem = null, bool ifCompleted = false) { switch (kind) { case MetadataType.TrafficMaterial: { - materialWorker.SetMaterialOK(itemID, ready, ifCompleted); + materialWorker.SetMaterialOK(itemID, ready, trafficItem, ifCompleted); break; } case MetadataType.TrafficAD: { @@ -72,27 +72,19 @@ namespace TrafficClient { break; } } + + Parameters?.MessageBus.Send(new TrafficAPIRefreshMessage()); } - public string SaveSegments(int itemID, MetadataType kind, List segments, List selectedSegments) { + public string SaveSegments(int itemID, MetadataType kind, List segments, List selectedSegments = null) { string result = null; switch (kind) { case MetadataType.TrafficMaterial: { if (true.Equals(Parameters?.Configuration?.MultiSegmentEnabled)) { materialWorker.SetMaterialOK(itemID, false); - int delSegCount = selectedSegments.Count - segments.Count; - while (delSegCount > 0) { - var segNr = (int)selectedSegments[selectedSegments.Count - 1].SegmentNr; - materialWorker.DeleteMaterialSegment(itemID, segNr); - selectedSegments.RemoveAt(selectedSegments.Count - 1); - delSegCount--; - } - - for (int i = 0; i < segments.Count; i++) { - MovieSegment actualSegment = segments[i]; - var segNr = (int)selectedSegments[i].SegmentNr; - result = materialWorker.AddMaterialSegment(itemID, actualSegment, result, segNr); - } + DeleteUnmappedSegments(itemID, segments, selectedSegments); + result = ModifySegments(itemID, segments, selectedSegments); + Parameters?.MessageBus.Send(new TrafficAPIRefreshMessage()); } else { //egyebkent nem fogadja el a szegmnesadatokat? @@ -117,6 +109,58 @@ namespace TrafficClient { return result; } + private string ModifySegments(int itemID, List segments, List selectedSegments) { + string result = null; + int segNr = 0; + string newSegmentName = null; + for (int i = 0; i < segments.Count; i++) { + MovieSegment actualSegment = segments[i]; + bool newSegment = selectedSegments.Count - 1 < i || (selectedSegments.Count == 1 && selectedSegments[0].SegmentId == null); + if (newSegment) { + segNr++; + } else { + if (selectedSegments[i].SegmentNr == null) + segNr++; + else + segNr = (int)selectedSegments[i].SegmentNr; + + } + + string fileName = null; + + //ha redifine volt, akkor minden modositott szegmens tartalmazza a fajlnevet + //ha define akkor az null, de a masodik szegmensnel mar az elsovel letrehozott fajlnev kell + if (!newSegment && actualSegment.SegID != 0) + fileName = actualSegment.MediaID; + + if (newSegment) { + if (actualSegment.MediaID == null) + fileName = newSegmentName; + else + fileName = actualSegment.MediaID; + } + + logger.Info("Adding segment {0} {1}", segNr, fileName ?? "null"); + result = materialWorker.AddMaterialSegment(itemID, actualSegment, fileName, segNr); + if (newSegment && newSegmentName == null) + newSegmentName = result; + } + + return newSegmentName ?? result; + } + + private void DeleteUnmappedSegments(int itemID, List segments, List selectedSegments) { + int delSegCount = selectedSegments.Count - segments.Count; + while (delSegCount > 0) { + if (selectedSegments[selectedSegments.Count - 1].SegmentId == null) + break; + var segId = (int)selectedSegments[selectedSegments.Count - 1].SegmentId; + materialWorker.DeleteMaterialSegment(itemID, segId); + selectedSegments.RemoveAt(selectedSegments.Count - 1); + delSegCount--; + } + } + public List GetPromos(string search, bool problematic, DateTime? from = null, DateTime? to = null) { return promoWorker.GetPromos(search, problematic, from, to); } diff --git a/client/PlanAIRClient/TrafficIDSelector.cs b/client/PlanAIRClient/TrafficIDSelector.cs index 0249fcb5..4981b581 100644 --- a/client/PlanAIRClient/TrafficIDSelector.cs +++ b/client/PlanAIRClient/TrafficIDSelector.cs @@ -1,4 +1,5 @@ using Interfaces; +using LinkDotNet.MessageHandling.Contracts; using MaestroShared.Commons; using MaestroShared.Configuration; using MaestroShared.MessageBus; @@ -35,6 +36,19 @@ namespace TrafficClient { rbMaterial.Text = metadata.Resource("MATERIAL", Resources.MATERIAL); rbPromo.Text = metadata.Resource("PROMOTION", Resources.PROMOTION); rbAD.Text = metadata.Resource("ADVERTISEMENT", Resources.ADVERTISEMENT); + parameters?.MessageBus.Subscribe(OnRefreshTrafficGrid); + } + + private void RefreshKeepPosition() { + int pos = dgTraffic.FirstDisplayedScrollingRowIndex; + RefreshResults(); + dgTraffic.FirstDisplayedScrollingRowIndex = pos; + } + + private void OnRefreshTrafficGrid(IMessage m) { + BeginInvoke((Action)(() => { + RefreshKeepPosition(); + })); } public TrafficParameters Parameters { @@ -48,11 +62,14 @@ namespace TrafficClient { } } + public bool MultiSegment { get => true.Equals(parameters?.Configuration?.MultiSegmentEnabled); } + public void RefreshResults() { if (refreshDisabled || trafficAPI == null) return; ClearSelection(); Cursor.Current = Cursors.WaitCursor; + dgTraffic.SuspendLayout(); DateTime? scheduledDate = null; if (this.dtScheduled.Checked) scheduledDate = this.dtScheduled.Value.Date; @@ -61,7 +78,7 @@ namespace TrafficClient { dgTraffic.Columns.Clear(); TrafficMetadata metadata = parameters?.Configuration ?? new TrafficMetadata(); if (rbMaterial.Checked) { - if (true.Equals(parameters?.Configuration?.MultiSegmentEnabled)) + if (MultiSegment) dgTraffic.Columns.AddRange(GetMultiMaterialColumns(metadata)); else dgTraffic.Columns.AddRange(GetSingleMaterialColumns(metadata)); @@ -79,6 +96,8 @@ namespace TrafficClient { trafficAPIBindingSource.DataSource = null; else trafficAPIBindingSource.DataSource = items; + SelectResult(); + dgTraffic.ResumeLayout(); Cursor.Current = Cursors.Default; } @@ -125,7 +144,7 @@ namespace TrafficClient { selectedCell.Value = !(bool)selectedCell.Value; bool selected = (bool)selectedCell.Value; - if (true.Equals(parameters?.Configuration?.MultiSegmentEnabled)) { + if (MultiSegment) { bool clear = true; List selectedOthers = null; if (selected) { @@ -157,7 +176,7 @@ namespace TrafficClient { } private void CreateVersionIfMissing(TrafficItem trafficItem) { - /* + if (String.IsNullOrEmpty(trafficItem.MediaID)) { var currentVersion = trafficAPI.GetMaterials(trafficItem.EpisodeID, false)?.FirstOrDefault(); if (currentVersion == null || String.IsNullOrEmpty(currentVersion.MediaID)) { @@ -171,23 +190,29 @@ namespace TrafficClient { trafficItem.VariantID = currentVersion.VariantID; } - if (trafficItem.VariantID == 0 || String.IsNullOrEmpty(trafficItem.MediaID)) + if (trafficItem.VariantID == 0 || string.IsNullOrEmpty(trafficItem.MediaID)) return; } else if (ModifierKeys.HasFlag(Keys.Shift)) { var currentVersion = trafficAPI.GetMaterials(trafficItem.EpisodeID, false)?.FirstOrDefault(); TrafficVersion newVersion = trafficAPI.CreateMaterialVersion(trafficItem.EpisodeID, true); if (newVersion != null) { - RefreshResults(); + RefreshKeepPosition(); } } - */ + + } + public void ClearLookup() { + ClearSelection(); + txtFilter.Text = ""; + dtScheduled.Checked = true; + chkProblematic.Checked = true; } public void ClearSelection() { foreach (DataGridViewRow r in dgTraffic.Rows) { TrafficItem item = r.DataBoundItem as TrafficItem; if (item.Selected) - r.Cells[0].Value = false; + OnSelectionChanged(r); } } @@ -231,13 +256,37 @@ namespace TrafficClient { } private void SelectResult() { + if (string.IsNullOrWhiteSpace(txtFilter.Text)) + return; List result = trafficAPIBindingSource.DataSource as List; if (result == null || result.Count == 0) { TrafficMetadata metadata = parameters?.Configuration ?? new TrafficMetadata(); MsgBox.Info(metadata.Resource("IDNOTEXISTS", Resources.IDNOTEXISTS)); } else { - OnSelectionChanged(dgTraffic.Rows[0]); - result[0].Selected = true; + + bool hasSelection = false; + string searchText = txtFilter.Text; + if (MultiSegment) { + if (MetadataTypeUtil.Guess(searchText) == MetadataType.TrafficMaterialSegment) + searchText = searchText.Substring(0, searchText.Length - 1); + } + + var results = result.Where(r => true.Equals(r.MediaID?.Contains(searchText))); + if (results != null) { + foreach (TrafficItem ti in results) { + foreach (DataGridViewRow row in dgTraffic.Rows) { + if (row.DataBoundItem == ti) { + hasSelection = true; + OnSelectionChanged(row); + if (!MultiSegment) + break; + } + } + } + } + + if (!hasSelection) + OnSelectionChanged(dgTraffic.Rows[0]); } } @@ -247,7 +296,6 @@ namespace TrafficClient { rbMaterial.Checked = true; refreshDisabled = false; RefreshResults(); - SelectResult(); } public void LookupByPromoID(string id) { @@ -256,7 +304,6 @@ namespace TrafficClient { rbPromo.Checked = true; refreshDisabled = false; RefreshResults(); - SelectResult(); } public void LookupByADID(string id) { @@ -265,7 +312,6 @@ namespace TrafficClient { rbAD.Checked = true; refreshDisabled = false; RefreshResults(); - SelectResult(); } private void OnRefresh(object sender, EventArgs e) { @@ -283,7 +329,13 @@ namespace TrafficClient { } public class TrafficAPIMessage : MaestroMessage { - public TrafficAPIMessage(string message) : base(message) { + public Exception Exception { get; } + + public TrafficAPIMessage(string message, Exception e) : base(message) { + Exception = e; } } + + public class TrafficAPIRefreshMessage : IMessage { + } } diff --git a/client/PlanAIRClient/TrafficIDSelectorColumns.cs b/client/PlanAIRClient/TrafficIDSelectorColumns.cs index 2858a86c..c40e0a56 100644 --- a/client/PlanAIRClient/TrafficIDSelectorColumns.cs +++ b/client/PlanAIRClient/TrafficIDSelectorColumns.cs @@ -4,10 +4,12 @@ using System.Windows.Forms; namespace TrafficClient { public partial class TrafficIDSelector { + const int CHECKWIDTH = 10; + DataGridViewColumn[] GetSingleMaterialColumns(TrafficMetadata metadata) { return new DataGridViewColumn[] { new DataGridViewCheckBoxColumn() { - AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + Width = CHECKWIDTH, DataPropertyName = "Selected", Frozen = true }, @@ -15,10 +17,11 @@ namespace TrafficClient { AutoSizeMode = DataGridViewAutoSizeColumnMode.None, DataPropertyName = "MediaID", HeaderText = metadata.Resource("TRAFFICID", Resources.TRAFFICID), - Width = 100 + Width = 100, + Frozen = true }, new DataGridViewCheckBoxColumn() { - AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + HeaderText = "OK", DataPropertyName = "OK", ReadOnly = true }, @@ -70,7 +73,7 @@ namespace TrafficClient { DataGridViewColumn[] GetMultiMaterialColumns(TrafficMetadata metadata) { return new DataGridViewColumn[] { new DataGridViewCheckBoxColumn() { - AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + Width = CHECKWIDTH, DataPropertyName = "Selected", Frozen = true }, @@ -78,13 +81,19 @@ namespace TrafficClient { AutoSizeMode = DataGridViewAutoSizeColumnMode.None, DataPropertyName = "MediaID", HeaderText = metadata.Resource("TRAFFICID", Resources.TRAFFICID), - Width = 100 + Width = 100, + Frozen = true, + }, + new DataGridViewCheckBoxColumn() { + HeaderText = "OK", + DataPropertyName = "OK", + ReadOnly = true }, new DataGridViewTextBoxColumn() { AutoSizeMode = DataGridViewAutoSizeColumnMode.None, DataPropertyName = "Segment", HeaderText = metadata.Resource("SEGMENT", Resources.SEGMENT), - Width = 100 + Width = 120 }, new DataGridViewTextBoxColumn() { AutoSizeMode = DataGridViewAutoSizeColumnMode.None, @@ -98,11 +107,6 @@ namespace TrafficClient { HeaderText = metadata.Resource("EPISODETITLE", Resources.EPISODETITLE), Width = 100 }, - new DataGridViewCheckBoxColumn() { - AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, - DataPropertyName = "OK", - ReadOnly = true - }, new DataGridViewTextBoxColumn() { AutoSizeMode = DataGridViewAutoSizeColumnMode.None, DataPropertyName = "EpisodeNumber", @@ -134,7 +138,7 @@ namespace TrafficClient { DataGridViewColumn[] GetADColumns(TrafficMetadata metadata) { return new DataGridViewColumn[] { new DataGridViewCheckBoxColumn() { - AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + Width = CHECKWIDTH, DataPropertyName = "Selected", Frozen = true }, @@ -142,10 +146,11 @@ namespace TrafficClient { AutoSizeMode = DataGridViewAutoSizeColumnMode.None, DataPropertyName = "MediaID", HeaderText = metadata.Resource("TRAFFICID", Resources.TRAFFICID), - Width = 100 + Width = 100, + Frozen = true }, new DataGridViewCheckBoxColumn() { - AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + HeaderText = "OK", DataPropertyName = "OK", ReadOnly = true }, @@ -173,6 +178,7 @@ namespace TrafficClient { DataGridViewColumn[] GetPromoColumns(TrafficMetadata metadata) { return new DataGridViewColumn[] { new DataGridViewCheckBoxColumn() { + Width = CHECKWIDTH, AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, DataPropertyName = "Selected", Frozen = true @@ -181,10 +187,11 @@ namespace TrafficClient { AutoSizeMode = DataGridViewAutoSizeColumnMode.None, DataPropertyName = "MediaID", HeaderText = metadata.Resource("TRAFFICID", Resources.TRAFFICID), - Width = 100 + Width = 100, + Frozen = true }, new DataGridViewCheckBoxColumn() { - AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells, + HeaderText = "OK", DataPropertyName = "OK", ReadOnly = true }, diff --git a/client/PlanAIRClient/Workers/ADWorker.cs b/client/PlanAIRClient/Workers/ADWorker.cs index 57e2b4ef..3bba28b3 100644 --- a/client/PlanAIRClient/Workers/ADWorker.cs +++ b/client/PlanAIRClient/Workers/ADWorker.cs @@ -29,7 +29,7 @@ namespace TrafficClient.Workers { return new TrafficItemMetadata() { EpisodeID = data.t_MediaID, EpisodeTitle = data.v_Title, - ProgID = data.t_SpotID.ToString(), + ProgID = data.t_MediaID, ProgTitle = data.v_Title }; } diff --git a/client/PlanAIRClient/Workers/MultiSegmentMaterialWorker.cs b/client/PlanAIRClient/Workers/MultiSegmentMaterialWorker.cs index e7646f1c..61443787 100644 --- a/client/PlanAIRClient/Workers/MultiSegmentMaterialWorker.cs +++ b/client/PlanAIRClient/Workers/MultiSegmentMaterialWorker.cs @@ -9,6 +9,8 @@ namespace TrafficClient.Workers { public class MultiSegmentMaterialWorker : SingleSegmentMaterialWorker { private static Logger logger = LogManager.GetCurrentClassLogger(); + protected override bool MultiSegmentEnabled { get => true; } + public MultiSegmentMaterialWorker(SqlConnection connection, PlanAirDataReader cliFSPReader, string functionName, IMessageBus messageBus) : base(connection, cliFSPReader, functionName, messageBus) { } diff --git a/client/PlanAIRClient/Workers/SingleSegmentMaterialWorker.cs b/client/PlanAIRClient/Workers/SingleSegmentMaterialWorker.cs index 95953902..b1175aba 100644 --- a/client/PlanAIRClient/Workers/SingleSegmentMaterialWorker.cs +++ b/client/PlanAIRClient/Workers/SingleSegmentMaterialWorker.cs @@ -12,6 +12,7 @@ namespace TrafficClient.Workers { public class SingleSegmentMaterialWorker : TrafficWorker { private static Logger logger = LogManager.GetCurrentClassLogger(); + protected virtual bool MultiSegmentEnabled { get => false; } public SingleSegmentMaterialWorker(SqlConnection connection, PlanAirDataReader cliFSPReader, string functionName, IMessageBus messageBus) : base(connection, cliFSPReader, functionName, messageBus) { @@ -37,7 +38,8 @@ namespace TrafficClient.Workers { TCIn = new Timecode((int)data.v_SegTcIn), TCOut = new Timecode((int)data.v_SegTcOut), Comment = data.v_SegTitle, - Optional = true.Equals(data.v_SegDropable) + Optional = true.Equals(data.v_SegDropable), + MediaID = data.v_SegMediaID }; } return result; @@ -168,23 +170,27 @@ namespace TrafficClient.Workers { get => 1100; } - public void SetMaterialOK(int itemID, bool ok, bool ifCompleted = false) { + public void SetMaterialOK(int itemID, bool ok, TrafficItem trafficItem = null, bool ifCompleted = false) { try { if (ifCompleted) { logger.Info("Checking if material {0} can be approved", itemID); - List items = GetMaterials(itemID.ToString(), false, null, null); + List items = GetMaterials(trafficItem.MediaID, false, null, null); + if (items == null || items.Count == 0) { logger.Info("Material {0} not exists", itemID); return; } + var allCount = items[0].SegmentCount; if (allCount == 0) { logger.Info("Material {0} has no segments", itemID); + return; } List segments = GetMaterialSegments(itemID); if (allCount != segments.Count) { logger.Info("Material {0} needs more segments", itemID); + return; } if (segments == null || segments.Count == 0) { @@ -218,6 +224,9 @@ namespace TrafficClient.Workers { private MovieSegment ToSegment(PlanAirMaterialSegmentResult item) { return new MovieSegment() { + SegNr = (int)item.v_SegNumber, + SegID = item.v_SegID, + MediaID = item.v_MediaID, TCIn = item.v_TcIn.HasValue ? new Timecode(item.v_TcIn.Value) : new Timecode(), TCOut = item.v_TcOut.HasValue ? new Timecode(item.v_TcOut.Value) : new Timecode(), Comment = item.v_SegTitle, @@ -293,6 +302,7 @@ namespace TrafficClient.Workers { } public string AddMaterialSegment(int itemID, MovieSegment segment, string fileName = null, int segNr = 0) { + string result = null; try { TryConnect(); using (SqlCommand cmd = CreateCommmad()) { @@ -311,14 +321,24 @@ namespace TrafficClient.Workers { if (fileName != null) cmd.Parameters.AddWithValue("@@StrParam3", fileName); cmd.Parameters.AddWithValue("@@@Options", optionalParam); - cmd.ExecuteNonQuery(); + + if (MultiSegmentEnabled) { + var reader = cmd.ExecuteReader(); + if (reader.Read()) { + int i = reader.GetOrdinal("v_MediaID"); + if (!reader.IsDBNull(i)) + result = reader.GetString(i); + } + } else + cmd.ExecuteNonQuery(); + //reader.DumpColumns(); } } catch (Exception e) { OnError(logger, e); } finally { connection.Close(); } - return null; + return result; } } diff --git a/client/PlanAIRClient/Workers/TrafficWorker.cs b/client/PlanAIRClient/Workers/TrafficWorker.cs index e96e6acd..ef26b1ef 100644 --- a/client/PlanAIRClient/Workers/TrafficWorker.cs +++ b/client/PlanAIRClient/Workers/TrafficWorker.cs @@ -29,7 +29,7 @@ namespace TrafficClient.Workers { protected void OnError(Logger logger, Exception e) { logger.Error(e); if (messageBus != null) - messageBus.Send(new TrafficAPIMessage("Sikertelen kapcsolódás a TRAFFIC rendszerhez.")); + messageBus.Send(new TrafficAPIMessage("Hiba a PlanAir kommunikációban. Rendszerüzenet: " + e.Message, e)); } protected SqlCommand CreateCommmad() { diff --git a/docs/mediacube-dxplay.md b/docs/mediacube-dxplay.md index a2211e53..8109e7b0 100644 --- a/docs/mediacube-dxplay.md +++ b/docs/mediacube-dxplay.md @@ -1,5 +1,6 @@ # MediaCube Maestro DxPlay -> *Verzió: 1.0 - 2018.09.03* +> *Dokumentum verzió: 1.1 - 2018.12.14* +> *Szoftver verzió: 2.0.8.7* A DxPlay alkalmazás lehetővé teszi a nagyfelbontású MXF fájlok lejátszását, és a megnyitott anyaghoz PlanAir szegmens adatok hozzárendelését. @@ -17,7 +18,11 @@ A fájl megnyitása után a média első képkockája válik láthatóvá a fel ## Metadat kiválasztása -Ha a megnyitott fájl neve egy létező azonosító, akkor a kereső automatikusan listázza és a listában kijelöli azt. Ismert azonosító esetén a felső keresősávba beírva azt és Enter-t nyomva listázhatók ki a találatok. Ha nincs azonosító beírva a keresősávba, akkor egy adásnap listázására van lehetőség, kiválasztva a típust (műsor, reklám, promó) továbbá a dátum választóban a napot. Ha azokra a bejegyzésekre vagyunk kiváncsiak, amikhez még nem társult a rendszerben verzió, szegmens, illetve nincsenek elfogadva, akkor a "problémásak" pipát is be kell jelölni. A lista automatikusan frissül a szűrők módosításának hatására, vagy a frissítés gombra kattintva. Az azonosító kiválasztáskor a rendszer automatikusan betölti a szegmens szerkesztőbe a már korábban létrehozott szegmensek adatait. +Alapértelmezett esetben a rendszer működése azt feltételezi, hogy egy műsor minden szegmense ugyan abban a fájlban találató meg. Ha a megnyitott fájl neve egy létező azonosító, akkor a kereső automatikusan listázza és a listában kijelöli azt. Ismert azonosító esetén a felső keresősávba beírva azt és Enter-t nyomva listázhatók ki a találatok. Ha nincs azonosító beírva a keresősávba, akkor egy adásnap listázására van lehetőség, kiválasztva a típust (műsor, reklám, promó) továbbá a dátum választóban a napot. Ha azokra a bejegyzésekre vagyunk kiváncsiak, amikhez még nem társult a rendszerben verzió, szegmens, illetve nincsenek elfogadva, akkor a "problémásak" pipát is be kell jelölni. A lista automatikusan frissül a szűrők módosításának hatására, vagy a frissítés gombra kattintva. Az azonosító kiválasztáskor a rendszer automatikusan betölti a szegmens szerkesztőbe a már korábban létrehozott szegmensek adatait, és ez a kiválasztott azonosító lesz majd a fájl neve. Az epizód kiválasztását a sorok elején látható pipára kattintva lehet elvégezni, de egyszerre csak egy bejegyzést lehet kiválasztani, erről a felület gondoskodik. + +### Multiszegmens opció + +A megfelelő beállítással át lehet kapcsolni a működést úgy, hogy a listában ne műsor/epizód bontás jelenjen meg, hanem műsor/epizód/szegmens bontás. Tehát ha a műsorhoz az adásszerkesztő előre létrehoz szegmenseket, akkor a listázóban annyi bejegyzés jelenik meg, ahány szegmens van. Ebben az esetben az ugyan ahhoz az epizódhoz tartozó szegmensek közül akármennyi kiválasztható, a felület ezt engedi. A fájlnévképzés is más, mert annak végére egy verzionáló szám kerül. Ha egy forráshoz a szegmensek egy részét hozzárendeljük, akkor azokat ugyan abban a fájlban adjuk meg. A szegmensek egy másik csoportját egy másik forráshoz hozzárendelve, az eredmény fájl nevének a vége megváltozik. Ha több szegmenst hozunk létre, mint amennyit a listában kiválasztottunk, akkor a plusz szegmensek a mentés után automatikusan létrejönnek az adásszerkesztő rendszerben. Ha kevesebb szegmenst hozunk létre, akkor a kiválasztott szegmensek listájában hátulról haladva törli a program a felesleges szegmenseket. ## Szegmensek szerkesztése A jobb oldali fülek közül a "Segments" nevűre kattintva jelenik meg a szegmens szerkesztő. @@ -25,17 +30,23 @@ A jobb oldali fülek közül a "Segments" nevűre kattintva jelenik meg a szegme ![Dxplay define segments](dxplay-define.png) A szerkesztő tetején elérhető funkciók rendre az alábbiak: -* Szegmens létrehozása: A teljes fájlra definiál egy szegmenst, kezdete a file kezdő timecode-ja, a vége pedig a kezdéshez hozzáadott hossz. +* Szegmens létrehozása: Az elérhető szabad területen definiál egy szegmenst. Ha a szegmenslista még üres, akkor az új szegmens kezdete a file kezdő timecode-ja, a vége pedig a kezdéshez hozzáadott hossz. Ha már van elem a listában, akkor a létrejövő szegmens kezdő timecode-ja az utolsó szegmens vége után egy képkockával kezdődik. * Aktuális pozíció belépőként: A kijelölt szegmens belépőjét átállítja az aktuális lejátszópozícióra. * Szegmens szétvágása: A kijelölt szegmensből kettőt készít, az első szegmens vége és a második szegmens eleje az aktuális lejátszópozíció lesz. * Aktuális pozíció kilépőként: A kijelölt szegmens kilépőjét átállítja az aktuális lejátszópozícióra. -* Szegmens törlése: A kijelölt szegmenst eltávolítja a listából. +* Szegmens törlése: A kijelölt szegmenst eltávolítja a listából, ha azt az aktuális szegmentálási foéyamatban adjuk hozzá a listához. + +A multiszegmens opciót használva a listán végrehajtható tevékenységek mindíg az adott fájra érvényesek. Más nevű fájlokhoz rendelt szegmensek nem szerkeszthetőek, ezek módosítását az újraszegmentálás funkcióval lehet elérni. +A szerkesztés véglegesítése a "Kész" (Approve) gombra kattintva történik meg, a fájl célmappába mozgatása után. -A szerkesztés véglegesítése a "Mentés" gombra kattintva történik meg, a fájl célmappába mozgatása után. +## Újraszegmentálás + +Ha már egy korábban feldolgozott műsort szeretnénk fájlmozgatás nélkül újraszegmentálni, akkor ezt a menüből lehet megtenni. Kiválasztva a funkciót megjelenik egy fájl tallózó ablak. A kívánt fájl megnyitása után, a "Kész" (Approve) gomb szövege megváltozik "Újraszegmentálás"-ra (Redefine). Az újraszegmentálás az adott fájlhoz tartozó szegmensek módosítását (belépő, kilépő) és törlését teszik csak lehetővé. A szegmentálást elvégezve a gombra kattintva, a fájl mozgatása nem történik meg, de a szegmensadatok mentésre kerülnek. ## Gyorsító billenytűk Ctrl+O : Fájl megnyitása. Ctrl+S : A változtatások elfogadása, tehát a fájl mozgatása a célmappába és a szegmens adatok mentése a PlanAir rendszerbe. +Ctrl+R : Szegmensek újradefiniálása. Space : Fájl lejátszása, lejátszás szüneteltetése. Ha a metaadat tallózó az aktív, akkor ott az aktuális metaadat azonosítójának kijelölése, vagy annak eltávolítása. Ha nincs verziója a kiválasztott bejegyzésnek, akkor létrehoz egyet. Shift+Space : Ha a metaadat tallózó az aktív, új verzió létrehozását végzi el a kijelölt anyagon. A lista automatikusan frissül utána. Esc: Teljes képernyős módban, kilép ablak módba. A teljes képrnyős módba lépéshez a képen duplán kell kattintani a bal egérgombbal. Ablak módban kilép az alkalmazás. @@ -60,6 +71,7 @@ A program __Configuration__ mappájában található meg a program beállítása "metadata": { "$type": "TrafficMetadata", "uiFileName": "dxplay.en", + "version": 0, "server": { "address": "Data Source=10.10.10.1;Initial Catalog=DBNAME;Persist Security Info=True;", "userName": "username", @@ -92,6 +104,11 @@ A tallózó típusa. A tallózó felületének honosított címkéit tartalmazó JSON állomány neve. > **dxplay.en** +### metadata.version +A metadat listázási módját és a szegmentálás módját határozza meg. 0 érték esetén az alpértelmezett műsor/epizód listázás az aktív, egy epizód összes szegmensét egy fájlhoz rendelhetjük csak hozzá. +1 érték esetén a műsor/epizód/szegmens listázás lesz aktív, és tetszőleges számú szegmens rendelhető tetszőleges számú fájlhoz. +> **0** + ### metadata.server A szolgáltatás elérhetősége a távoli szerveren. diff --git a/server/-configuration/run-mediacube-server-bsh.launch b/server/-configuration/run-mediacube-server-bsh.launch index b004926b..60593dc9 100644 --- a/server/-configuration/run-mediacube-server-bsh.launch +++ b/server/-configuration/run-mediacube-server-bsh.launch @@ -19,7 +19,7 @@ - + diff --git a/server/-dependencies/libs/solr-solrj-7.5.0.jar b/server/-dependencies/libs/solr-solrj-7.5.0.jar new file mode 100644 index 00000000..33c194c1 Binary files /dev/null and b/server/-dependencies/libs/solr-solrj-7.5.0.jar differ diff --git a/server/-dependencies/pom.xml b/server/-dependencies/pom.xml index ceb004d3..724ea60f 100644 --- a/server/-dependencies/pom.xml +++ b/server/-dependencies/pom.xml @@ -108,6 +108,20 @@ jar + + + + + + + + + + + + + + @@ -120,7 +134,7 @@ - + humble.video:linux:0.2.1 @@ -211,9 +225,6 @@ com.ibm:db2jcc4:4.19.26 - - - org.slf4j:slf4j-simple:1.6.3 diff --git a/server/hu.user.mediacube.executors.tests/META-INF/MANIFEST.MF b/server/hu.user.mediacube.executors.tests/META-INF/MANIFEST.MF index cbb7f2c4..2fa649d6 100644 --- a/server/hu.user.mediacube.executors.tests/META-INF/MANIFEST.MF +++ b/server/hu.user.mediacube.executors.tests/META-INF/MANIFEST.MF @@ -5,4 +5,5 @@ Bundle-SymbolicName: hu.user.mediacube.executors.tests Bundle-Version: 1.0.0.qualifier Fragment-Host: user.jobengine.executors;bundle-version="1.0.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.junit +Import-Package: org.apache.commons.io.filefilter;version="2.2.0", + org.junit diff --git a/server/hu.user.mediacube.executors.tests/src/hu/user/mediacube/executors/tests/RescueNEXIOMaterials.java b/server/hu.user.mediacube.executors.tests/src/hu/user/mediacube/executors/tests/RescueNEXIOMaterials.java new file mode 100644 index 00000000..50ff8757 --- /dev/null +++ b/server/hu.user.mediacube.executors.tests/src/hu/user/mediacube/executors/tests/RescueNEXIOMaterials.java @@ -0,0 +1,328 @@ +package hu.user.mediacube.executors.tests; + +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPReply; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; + +import com.ibm.nosql.json.api.BasicDBObject; +import com.ibm.nosql.json.api.DB; +import com.ibm.nosql.json.api.DBObject; + +import user.commons.CalendarUtils; +import user.commons.StoreUri; +import user.commons.nosql.NoSQLUtils; +import user.commons.octopus.IOctopusAPI; +import user.commons.octopus.OctopusAPI; +import user.commons.remotestore.FtpDirectoryLister; +import user.commons.remotestore.RemoteStoreProtocol; +import user.jobengine.db.IItemManager; +import user.jobengine.server.steps.EscortFiles; +import user.jobengine.server.steps.FileArchive; +import user.jobengine.server.steps.RundownArchive; +import user.jobengine.server.steps.StoryArchive; + +public class RescueNEXIOMaterials { + private static final String SCHEDULED_FORMAT = "yyyy.MM.dd HH:mm"; + private static final Logger logger = LogManager.getLogger(); + private static final String UTF_8 = "utf-8"; + private static final String JSON_EXT = ".json"; + private static final String XML_EXT = ".xml"; + private static final String DURATION = "duration"; + private static final String MXFEXT = ".MXF"; + private static final String NEXIOCLIPS = "nexioclips"; + private static final String LONGNAMEID = "longnameid"; + private static final String ID = "id"; + private static final String MEDIATYPE = "Hír bejátszó"; + private OctopusAPI octopusAPI; + + private DB db; + private FTPClient sourceFtp; + private FTPClient targetFtp; + private StoreUri sourceUri; + private StoreUri targetUri; + private int nexioKillDateDays; + private String nexioAgency; + private Marker systemMarker; + private List transferredFileNames = null; + private boolean demo = false; + + public void copy(RundownArchive rundownArchive) throws Exception { + for (StoryArchive storyArchive : rundownArchive.getStoryArchives()) { + for (FileArchive fileArchive : storyArchive.getFileArchives()) { + try { + copyFile(fileArchive, rundownArchive, storyArchive); + } catch (Exception e) { + logger.error(systemMarker, "A '{}' clip archiválása sikertelen. A rendszer üzenete: {}", fileArchive.getFileName(), e.getMessage()); + throw e; + } + } + } + } + + private void copyFile(FileArchive fileArchive, RundownArchive rundownArchive, StoryArchive storyArchive) throws Exception { + String origFileName = fileArchive.getFileName(); + String fileName = String.format("%s-%s", origFileName, rundownArchive.getItemHouseId()); + String videoFileName = fileName + MXFEXT; + + if (transferredFileNames == null) + transferredFileNames = new ArrayList<>(); + + //A mar letezo mozikat nem archivaljuk le ujra, csak a metaadatot + long existingMediaId = 0; + if (!transferredFileNames.contains(origFileName)) { + transferredFileNames.add(origFileName); + if (!demo) + transferFile(origFileName + MXFEXT, videoFileName); + logger.info(systemMarker, "A '{}' file archiválásra felkészítése sikeres volt.", origFileName); + } + + if (!demo) { + BasicDBObject metadata = createMetadata(rundownArchive, storyArchive, fileArchive, existingMediaId); + transferMetadata(videoFileName, metadata); + createSourceKillDateFile(rundownArchive, origFileName); + } + } + + private BasicDBObject createMetadata(RundownArchive rundownArchive, StoryArchive storyArchive, FileArchive fileArchive, long existingMediaId) { + BasicDBObject result = new BasicDBObject(); + result.put("itemHouseId", rundownArchive.getItemHouseId()); + result.put("itemTitle", rundownArchive.getItemTitle()); + result.put("itemDescription", rundownArchive.getItemDesc()); + result.put("userName", "mediacube"); + result.put("mediaHouseId", storyArchive.getMediaHouseId()); + result.put("mediaTitle", storyArchive.getMediaTitle()); + result.put("mediaDescription", storyArchive.getMediaDesc()); + result.put("mediaType", MEDIATYPE); + result.put("duration", fileArchive.getDuration()); + result.put("existingMediaId", existingMediaId); + return result; + } + + private void createSourceKillDateFile(RundownArchive rundownArchive, String fileName) throws Exception { + logger.info("Create killdate/agency for {}", fileName); + OutputStream outStream = null; + try { + sourceFtp = ((FtpDirectoryLister) sourceUri.getLister()).connect(); + Calendar killDate = CalendarUtils.createCalendar(rundownArchive.getScheduleDate()); + killDate.add(Calendar.DAY_OF_YEAR, nexioKillDateDays); + byte[] killDateFile = EscortFiles.createNEXIOKillDateFile(fileName, killDate.getTime(), null, nexioAgency); + outStream = sourceFtp.storeFileStream(fileName + XML_EXT); + if (outStream == null) { + throw new NullPointerException("Can not open: " + fileName + XML_EXT + " Reply:" + sourceFtp.getReplyString()); + } + outStream.write(killDateFile); + outStream.flush(); + } catch (Exception e) { + logger.catching(e); + throw e; + } finally { + if (outStream != null) + outStream.close(); + sourceUri.cleanUp(); + } + } + + private FileArchive processMosObject(BasicDBObject rundown, BasicDBObject story, BasicDBObject mosObject, String clipName, long duration) throws Exception { + String mosID = mosObject.getString(IOctopusAPI.OBJ_ID); + if (!mosID.equals(clipName)) + return null; + return new FileArchive(mosID, duration); + } + + public RundownArchive processRundow(String clipName, long duration) throws Exception { + octopusAPI = new OctopusAPI(); + List rds = octopusAPI.getRundownsByPlaceHolderID(clipName); + if (rds == null || rds.size() == 0) + return null; + BasicDBObject rundown = (BasicDBObject) rds.get(0); + long rundownID = rundown.getLong(ID); + if (!demo) + logger.info("Processing rundown {} {}", rundownID, rundown.getString(IOctopusAPI.NAME)); + List stories = octopusAPI.getRundownFullStories(rundownID); + if (stories == null) + return null; + RundownArchive result = new RundownArchive(); + + long id = NoSQLUtils.asLong(rundown, IOctopusAPI.ID); + if (id == 0) + return null; + String name = NoSQLUtils.asString(NoSQLUtils.asDBObject(rundown, IOctopusAPI.RUNDOWN_TYPE), IOctopusAPI.NAME); + if (StringUtils.isBlank(name)) + return null; + String channel = NoSQLUtils.asString(NoSQLUtils.asDBObject(rundown, IOctopusAPI.CHANNEL), IOctopusAPI.NAME); + Date scheduledStart = rundown.getDate(IOctopusAPI.SCHEDULED_START); + if (scheduledStart == null) + return null; + String start = CalendarUtils.toString(CalendarUtils.createCalendar(scheduledStart), SCHEDULED_FORMAT); + result.setScheduleDate(scheduledStart); + result.setItemHouseId(String.valueOf(id)); + result.setItemTitle(String.format("%s %s %s", start, name, channel)); + + for (DBObject s : stories) { + StoryArchive storyArchive = processStory(rundown, s, clipName, duration); + if (storyArchive == null) + continue; + result.addStoryArchive(storyArchive); + break; + } + return result; + } + + private StoryArchive processStory(BasicDBObject rundown, DBObject s, String clipName, long duration) throws Exception { + BasicDBObject story = (BasicDBObject) s; + String parentStoryID = story.getString(IOctopusAPI.PARENT_STORY_ID); + if (StringUtils.isBlank(parentStoryID)) { + logger.warn("Story parentStoryID is null: {}", story.toPrettyString(null)); + return null; + } else + logger.debug("Processing story {}", parentStoryID); + List mosObjects = NoSQLUtils.asList(story, IOctopusAPI.MOS_OBJECTS); + if (mosObjects == null) + return null; + + StoryArchive storyArchive = null; + for (BasicDBObject mosObject : mosObjects) { + FileArchive fileArchive = processMosObject(rundown, story, mosObject, clipName, duration); + if (fileArchive == null) + continue; + if (storyArchive == null) { + storyArchive = new StoryArchive(); + storyArchive.setMediaHouseId(parentStoryID); + storyArchive.setMediaTitle(story.getString(IOctopusAPI.NAME)); + storyArchive.setMediaDesc(story.getString(IOctopusAPI.SCRIPT_CONTENT)); + + } + storyArchive.addFileArchive(fileArchive); + } + return storyArchive; + + } + + public void set(int nexioPort, String nexioUserName, String nexioPassword, String archiveFtp, String archiveUserName, String archivePassword, + int nexioKillDateDays, String nexioAgency, IItemManager manager) throws Exception { + db = NoSQLUtils.getNoSQLDB(); + if (db == null) { + logger.error(systemMarker, "Az NoSQL adatkezelő réteg nem elérhető."); + throw new NullPointerException("Internal error, missing NoSQL DB reference."); + } + + if (manager == null) { + logger.error(systemMarker, "Az adatbáziskezelő réteg nem elérhető."); + throw new NullPointerException("Internal error, missing ItemManager reference."); + } + String nexioHost = System.getProperty("nexio.host"); + if (StringUtils.isBlank(nexioHost)) { + logger.error(systemMarker, "A 'nexio.host' rendszer paraméter nem található."); + throw new NullPointerException("System is not configured properly, 'jobengine.selenio.address' startup parameter missing."); + } + this.nexioKillDateDays = nexioKillDateDays; + this.nexioAgency = nexioAgency; + + sourceUri = manager.createStoreUri(RemoteStoreProtocol.FTP, nexioHost); + sourceUri.setPortNumber(nexioPort); + sourceUri.setUserName(nexioUserName); + sourceUri.setPassword(nexioPassword); + if (sourceUri == null) { + logger.error(systemMarker, "A forrás nem elérhető."); + throw new NullPointerException("Internal error, missing 'sourceUri'."); + } + + targetUri = manager.createStoreUri(new URI(archiveFtp)); + targetUri.setUserName(archiveUserName); + targetUri.setPassword(archivePassword); + if (targetUri == null) { + logger.error(systemMarker, "A cél nem elérhető."); + throw new NullPointerException("Internal error, missing 'targetUri'."); + } + + } + + private void transferFile(String sourceFileName, String targetFileName) throws Exception { + int reply = 0; + logger.info("Transfer clip {}", sourceFileName); + try { + sourceFtp = ((FtpDirectoryLister) sourceUri.getLister()).connect(); + targetFtp = ((FtpDirectoryLister) targetUri.getLister()).connect(); + if (!targetFtp.enterRemotePassiveMode()) + throw new Exception("!PASV"); + + reply = sourceFtp.port(InetAddress.getByName(targetFtp.getPassiveHost()), targetFtp.getPassivePort()); + if (!FTPReply.isPositiveCompletion(reply)) + throw new Exception("PORT parancs válasza: " + sourceFtp.getReplyString()); + + if (!sourceFtp.setFileType(FTP.BINARY_FILE_TYPE)) + throw new Exception("!SOURCE TYPE"); + + reply = sourceFtp.retr(sourceFileName); + if (!FTPReply.isPositivePreliminary(reply)) + throw new Exception("RETR parancs válasza: " + sourceFtp.getReplyString()); + + if (!targetFtp.setFileType(FTP.BINARY_FILE_TYPE)) + throw new Exception("!TARGET TYPE"); + + reply = targetFtp.stor(targetFileName); + if (!FTPReply.isPositivePreliminary(reply)) + throw new Exception("STOR parancs válasza: " + sourceFtp.getReplyString()); + + while (true) { + reply = sourceFtp.stat(); + if (!FTPReply.isPositiveCompletion(reply)) + throw new Exception("STAT parancs válasza: " + sourceFtp.getReplyString()); + + logger.info("Status: {}", sourceFtp.getReplyString()); + if (reply == 226) { + break; + } + Thread.sleep(1000); + } + } catch (Exception e) { + logger.catching(e); + throw e; + } finally { + sourceUri.cleanUp(); + targetUri.cleanUp(); + } + + } + + private void transferMetadata(String fileName, BasicDBObject metadata) throws Exception { + logger.info("Transfer metadata {}", fileName); + OutputStream outStream = null; + try { + targetFtp = ((FtpDirectoryLister) targetUri.getLister()).connect(); + if (!targetFtp.changeWorkingDirectory(EscortFiles.STATUSFOLDER)) { + targetFtp.makeDirectory(EscortFiles.STATUSFOLDER); + if (!targetFtp.changeWorkingDirectory(EscortFiles.STATUSFOLDER)) + throw new Exception("!STATUSFOLDER"); + } + + outStream = targetFtp.storeFileStream(fileName + JSON_EXT); + if (outStream == null) { + throw new NullPointerException("Can not open: " + fileName + JSON_EXT + " Reply:" + targetFtp.getReplyString()); + } + outStream.write(metadata.toString().getBytes(UTF_8)); + outStream.flush(); + //targetFtp.changeToParentDirectory(); + } catch (Exception e) { + logger.catching(e); + throw e; + } finally { + if (outStream != null) + outStream.close(); + targetUri.cleanUp(); + } + } + +} diff --git a/server/hu.user.mediacube.executors.tests/src/hu/user/mediacube/executors/tests/Support.java b/server/hu.user.mediacube.executors.tests/src/hu/user/mediacube/executors/tests/Support.java index 7c7ca260..9d66e965 100644 --- a/server/hu.user.mediacube.executors.tests/src/hu/user/mediacube/executors/tests/Support.java +++ b/server/hu.user.mediacube.executors.tests/src/hu/user/mediacube/executors/tests/Support.java @@ -4,9 +4,15 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; +import java.net.URI; +import java.nio.file.DirectoryStream; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; @@ -15,13 +21,27 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import com.ibm.nosql.json.api.BasicDBList; +import com.ibm.nosql.json.api.BasicDBObject; +import com.ibm.nosql.json.api.DB; +import com.ibm.nosql.json.api.DBCollection; +import com.ibm.nosql.json.api.DBCursor; +import com.ibm.nosql.json.api.QueryBuilder; + +import user.commons.ListUtils; import user.commons.logging.LogUtils; +import user.commons.nosql.NoSQLUtils; import user.jobengine.db.IItemManager; +import user.jobengine.db.IResultSetConsumer; import user.jobengine.db.Item; import user.jobengine.db.ItemManager; import user.jobengine.db.Media; import user.jobengine.db.MediaFile; +import user.jobengine.server.steps.ItemManagerExtensions; +import user.jobengine.server.steps.MetadataTypeDetector; +import user.jobengine.server.steps.MetadataTypeDetector.MetadataType; import user.jobengine.server.steps.PlanAirExtensions; +import user.jobengine.server.steps.RundownArchive; public class Support { @@ -32,13 +52,17 @@ public class Support { // System.setProperty("jobengine.octopus.rundowns.name", "test_rundowns"); // System.setProperty("jobengine.octopus.stories.name", "test_stories"); // System.setProperty("jobengine.octopus.storyfolders.name", "test_storyfolders"); - System.setProperty("jobengine.octopus.rundowns.name", "rundowns180620"); - System.setProperty("jobengine.octopus.stories.name", "stories180620"); - System.setProperty("jobengine.octopus.storyfolders.name", "storyfolders180620"); + // System.setProperty("jobengine.octopus.rundowns.name", "rundowns180620"); + // System.setProperty("jobengine.octopus.stories.name", "stories180620"); + // System.setProperty("jobengine.octopus.storyfolders.name", "storyfolders180620"); + System.setProperty("jobengine.octopus.rundowns.name", "rundowns181217"); + System.setProperty("jobengine.octopus.stories.name", "stories181217"); + System.setProperty("jobengine.octopus.storyfolders.name", "storyfolders181217"); System.setProperty("jobengine.nosql.db.url", "jdbc:db2://10.10.1.27:50000/mc:retrieveMessagesFromServerOnGetMessage=true;"); System.setProperty("jobengine.nosql.db.user", "db2admin"); System.setProperty("jobengine.nosql.db.password", "password"); + System.setProperty("nexio.host", "10.10.1.55"); System.setProperty(ItemManager.DBURL, "jdbc:db2://10.10.1.27:50000/mc:retrieveMessagesFromServerOnGetMessage=true;"); System.setProperty(ItemManager.DBUSERNAME, "db2admin"); System.setProperty(ItemManager.DBPASSWORD, "password"); @@ -54,6 +78,122 @@ public class Support { manager.disconnect(); } + @Test + public void check_rd_archives() throws Exception { + DirectoryStream stream = null; + URI sourcePath = new URI("file://10.10.1.100/BRAAVOS/ARCHIVE"); + try { + stream = Files.newDirectoryStream(Paths.get(sourcePath)); + List allFiles = new ArrayList<>(); + for (Path p : stream) { + check_rd_collect(p, allFiles); + } + stream.close(); + + stream = Files.newDirectoryStream(Paths.get(sourcePath)); + for (Path p : stream) { + check_rd_process(p, allFiles); + } + stream.close(); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + + } + + private void check_rd_collect(Path mediaFilePath, List allFiles) { + File mediaFile = mediaFilePath.toFile(); + if (mediaFile.isDirectory() || !mediaFile.getName().toUpperCase().endsWith(".MXF")) + return; + allFiles.add(mediaFile); + } + + private File check_rd_nonzero_fellow(String mediaid, List allFiles) { + for (File mediaFile : allFiles) { + String houseid = mediaFile.getName().substring(0, mediaFile.getName().lastIndexOf("-")); + if (houseid.equals(mediaid) && mediaFile.length() > 0) + return mediaFile; + } + return null; + } + + private void check_rd_process(Path mediaFilePath, List allFiles) { + File mediaFile = mediaFilePath.toFile(); + if (mediaFile.isDirectory() || !mediaFile.getName().toUpperCase().endsWith(".MXF")) + return; + Path status = Paths.get(mediaFilePath.getParent().toString(), ".STATUS", mediaFile.getName() + ".json"); + if (!status.toFile().exists()) { + System.out.println("Nincs JSON: " + mediaFile.getAbsolutePath()); + } + + if (mediaFile.length() == 0) { + String houseid = mediaFile.getName().substring(0, mediaFile.getName().lastIndexOf(".")); + final String idToCheck = houseid.substring(0, houseid.lastIndexOf("-")); + MetadataType metadataType = MetadataTypeDetector.GuessMetadataType(idToCheck); + if (metadataType == MetadataType.OctopusPlaceholder) { + String query = String.format("select count(*) from mediafile where houseid like '%s%%'", idToCheck); + IResultSetConsumer consumer = rs -> { + if (rs.getInt(1) == 0) { + + File f = check_rd_nonzero_fellow(idToCheck, allFiles); + if (f == null) + System.out.println("0 még sincs korábbi: " + mediaFile.getAbsolutePath()); + + } + return false; + }; + manager.executeQuery(query, consumer, null); + } + + } + + } + + private void rescue_archive(RescueNEXIOMaterials processor, String clipName, long duration) throws Exception { + /* + int nexioPort, String nexioUserName, String nexioPassword, String archiveFtp, String archiveUserName, String archivePassword, + int nexioKillDateDays, String nexioAgency, IItemManager manager + */ + + RundownArchive ra = processor.processRundow(clipName, duration); + if (ra != null) { + System.out.println(ra.getItemTitle()); + //processor.copy(ra); + } else + System.out.println("No rundown for " + clipName); + + } + + @Test + public void rescue_rd_clips() throws Exception { + DB db = NoSQLUtils.getNoSQLDB(); + DBCollection collection = db.getCollection("nexioclips"); + BasicDBList agencies = new BasicDBList(); + agencies.add("ARCHIVED"); + QueryBuilder queryBuilder = QueryBuilder.start("extagency").in(agencies); + DBCursor cursor = collection.find(queryBuilder.get()); + int count = 0; + RescueNEXIOMaterials processor = new RescueNEXIOMaterials(); + processor.set(2098, "administrator", "system", "ftp://10.10.1.100/ARCHIVE/TEST", "mediacube", "Broadca5T", 21, "ARCHIVED", manager); + + List clips = ListUtils.cast(cursor.toArray()); + for (BasicDBObject clip : clips) { + String name = clip.getString("longnameid"); + long duration = clip.getLong("duration"); + if (name.length() > 1) { + + long existingMediaId = ItemManagerExtensions.getExistingRundownMedia(manager, name); + if (existingMediaId == 0) { + rescue_archive(processor, name, duration); + System.out.println(name); + count++; + } + } + + } + System.out.println(count); + } + @Test public void test_PlanAirExtensions() throws Exception { String actual = PlanAirExtensions.getMorpeusXML(manager, "jdbc:sqlserver://10.10.1.45;databaseName=PA_Echo;", "MAM", "Echotv.hu", "M009729A", "ISILON"); @@ -64,10 +204,14 @@ public class Support { @Test public void test_Solr() throws Exception { //http://lucene.apache.org/solr/guide/7_5/using-solrj.html#using-solrj + + final String solrUrl = "http://localhost:8983/solr"; + //return new HttpSolrClient.Builder(solrUrl).withConnectionTimeout(10000).withSocketTimeout(60000).build(); + ResteasyClient client = new ResteasyClientBuilder().build(); - ResteasyWebTarget webTarget = client.target(""); + ResteasyWebTarget webTarget = client.target("httpp:"); - String query = "SELECT * FROM MEDIADESCRIPTION FETCH FIRST ROW ONLY"; + String query = "SELECT * FROM MEDIADESCRIPTION FETCH FIRST 10 ROWS ONLY"; manager.executeQuery(query, rs -> { rs.getLong("itemid"); rs.getLong("mediaid"); @@ -97,4 +241,5 @@ public class Support { System.out.println("itemid=" + i.getId() + " AND mediaid=" + m.getId() + " AND mediafileid=" + mf.getId()); } + } diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/ArchiveListBuilderStep.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/ArchiveListBuilderStep.java index 79497481..36b092f9 100644 --- a/server/user.jobengine.executors/src/user/jobengine/server/steps/ArchiveListBuilderStep.java +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/ArchiveListBuilderStep.java @@ -93,7 +93,7 @@ public class ArchiveListBuilderStep extends JobStep { try { DirectoryStream stream = Files.newDirectoryStream(Paths.get(sourcePath)); for (Path p : stream) { - boolean processed = processPathItem(p, archiveList); + processPathItem(p, archiveList); } } catch (Exception e) { logger.catching(e); diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/ArchiveListBuilderStep2.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/ArchiveListBuilderStep2.java new file mode 100644 index 00000000..0f93d62d --- /dev/null +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/ArchiveListBuilderStep2.java @@ -0,0 +1,200 @@ +package user.jobengine.server.steps; + +import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; + +import com.ibm.nosql.json.JSONUtil; +import com.ibm.nosql.json.api.BasicDBObject; + +import user.commons.nosql.NoSQLUtils; +import user.jobengine.server.IJobEngine; +import user.jobengine.server.IJobRuntime; + +/** + * Az archivalhato mediak listazasa MediaFileWrapper objektumokban. A listazott media allomanyokat megjeloli .catched signal allomannyal, hogy a legkozelebbi + * listazas figyelmen kivul hagyja. + * + * @author robi + */ +public class ArchiveListBuilderStep2 extends JobStep { + private static final Logger logger = LogManager.getLogger(); + // private static final String UTF8 = "utf-8"; + private static final String STATUSFOLDER = ".STATUS"; + private static final String MXFEXT = ".mxf"; + private static final String WAVEXT = ".wav"; + private static final String JSONEXT = ".json"; + private static final String CATCHEDEXT = ".catched"; + + public static final String ITEM_TITLE = "itemTitle"; + public static final String ITEM_HOUSEID = "itemHouseId"; + public static final String ITEM_DESCRIPTION = "itemDescription"; + public static final String MEDIA_HOUSEID = "mediaHouseId"; + public static final String MEDIA_TITLE = "mediaTitle"; + public static final String MEDIA_DESCRIPTION = "mediaDescription"; + public static final String MEDIA_TYPE = "mediaType"; + private static final String DURATION = "duration"; + private static final String EXISTING_MEDIAID = "existingMediaId"; + + private Marker marker; + + private ArchiveItem createArchiveItem(Path jsonFilePath, Path mediaFilePath, Path catchedFilePath) { + ArchiveItem result = null; + byte[] readAllBytes = null; + try { + readAllBytes = Files.readAllBytes(jsonFilePath); + BasicDBObject dbObject = (BasicDBObject) JSONUtil.jsonToDbObject(new String(readAllBytes)); + if (dbObject == null) + throw new NullPointerException("Can not parse JSON file: " + jsonFilePath); + result = new ArchiveItem(); + result.setItemHouseId(getMetadata(dbObject, ITEM_HOUSEID)); + result.setItemTitle(getMetadata(dbObject, ITEM_TITLE)); + result.setItemDescription(getMetadata(dbObject, ITEM_DESCRIPTION)); + result.setMediaHouseId(getMetadata(dbObject, MEDIA_HOUSEID)); + result.setMediaTitle(getMetadata(dbObject, MEDIA_TITLE)); + result.setMediaDescription(getMetadata(dbObject, MEDIA_DESCRIPTION)); + result.setMediaType(getMetadata(dbObject, MEDIA_TYPE)); + result.setMediaFile(mediaFilePath.toString()); + result.setCatchedFile(catchedFilePath.toString()); + result.setDuration(NoSQLUtils.asLong(dbObject, DURATION)); + result.setExistingMediaId(NoSQLUtils.asLong(dbObject, EXISTING_MEDIAID)); + } catch (IOException e) { + logger.catching(e); + return null; + } + + return result; + } + + private void createCatchedFile(Path catchedFilePath) { + try { + Files.createFile(catchedFilePath); + //Files.write(catchedFilePath, CATCHED.getBytes(UTF8), StandardOpenOption.CREATE); + } catch (Exception e) { + logger.catching(e); + } + } + + @StepEntry + public Object[] execute(String sourcePath, int limit, IJobEngine jobEngine, IJobRuntime jobRuntime) { + marker = jobRuntime.getMarker(); + List archiveList = new LinkedList(); + DirectoryStream directoryStream = null; + try { + DirectoryStream stream = Files.newDirectoryStream(Paths.get(sourcePath)); + for (Path p : stream) { + processPathItem(p, archiveList); + } + } catch (Exception e) { + logger.catching(e); + logger.error(marker, "Az '{}' mappa elérése sikertelen. A rendszer hibaüzenete: {}", e.getMessage()); + } finally { + if (directoryStream != null) { + try { + directoryStream.close(); + } catch (IOException e) { + } + } + } + + if (archiveList.size() > 0) { + + if (limit > 0) { + List limitedList = new ArrayList<>(); + int realItemCount = 0; + for (ArchiveItem archiveItem : archiveList) { + File f = new File(archiveItem.getMediaFile()); + if (f.length() > 0) { + realItemCount++; + limitedList.add(archiveItem); + if (realItemCount == limit) + break; + } else + limitedList.add(archiveItem); + } + archiveList = limitedList; + logger.info(marker, "A folyamat elérte a beállított {} limitet.", limit); + } else + logger.info(marker, "Az archiváló folyamat {} új anyagot érzékelt.", archiveList == null ? 0 : archiveList.size()); + + for (ArchiveItem archiveItem : archiveList) { + createCatchedFile(Paths.get(archiveItem.getCatchedFile())); + } + } else + logger.info(marker, "Nincs archiválandó anyag."); + + return new Object[] { archiveList }; + } + + private String getMetadata(BasicDBObject dbObject, String fieldName) { + String result = null; + if (dbObject.containsKey(fieldName)) + result = dbObject.getString(fieldName); + return result; + } + + private boolean processPathItem(Path mediaFilePath, final List archiveList) { + File mediaFile = mediaFilePath.toFile(); + + if (mediaFile.isDirectory() || !mediaFile.getName().toLowerCase().endsWith(MXFEXT.toLowerCase()) + || mediaFile.getName().toLowerCase().endsWith(WAVEXT.toLowerCase())) { + return false; + } + + Path dotStorePath = Paths.get(mediaFilePath.getParent().toString(), STATUSFOLDER); + Path catchedFilePath = Paths.get(dotStorePath.toString(), mediaFile.getName() + CATCHEDEXT); + File catchedFile = catchedFilePath.toFile(); + if (catchedFile.exists()) { + logger.warn("{} file is already catched.", mediaFile.getName()); + return false; + } + + Path jsonFilePath = Paths.get(dotStorePath.toString(), mediaFile.getName() + JSONEXT); + File jsonFile = jsonFilePath.toFile(); + if (!jsonFile.exists()) { + logger.warn("{} has no json metadata.", mediaFile.getName()); + return false; + } + + ArchiveItem archiveItem = createArchiveItem(jsonFilePath, mediaFilePath, catchedFilePath); + + if (archiveItem == null) { + logger.warn("{} has no metadata specified.", mediaFile.getName()); + return false; + } + + if (StringUtils.isBlank(archiveItem.getItemHouseId())) { + logger.warn("{} has no Item HouseID specified in metadata.", mediaFile.getName()); + return false; + } + + if (StringUtils.isBlank(archiveItem.getItemTitle())) { + logger.warn("{} has no Item Title specified in metadata.", mediaFile.getName()); + return false; + } + + if (StringUtils.isBlank(archiveItem.getMediaHouseId())) { + logger.warn("{} has no Media HouseID specified in metadata.", mediaFile.getName()); + return false; + } + + if (StringUtils.isBlank(archiveItem.getMediaTitle())) { + logger.warn("{} has no Media Title specified in metadata.", mediaFile.getName()); + return false; + } + + archiveList.add(archiveItem); + return true; + } +} diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/CleanupMountedLocationStep.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/CleanupMountedLocationStep.java index 4dbef381..c754867f 100644 --- a/server/user.jobengine.executors/src/user/jobengine/server/steps/CleanupMountedLocationStep.java +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/CleanupMountedLocationStep.java @@ -34,6 +34,7 @@ public class CleanupMountedLocationStep extends JobStep implements FileVisitor

transferredFileNames = null; + private boolean demo = false; private int check(int value, String name) { if (value == 0) { @@ -84,6 +85,7 @@ public class CopyForArchiveNEXIOMaterialsStep extends JobStep { copyFile(fileArchive, rundownArchive, storyArchive); } catch (Exception e) { logger.error(systemMarker, "A '{}' clip archiválása sikertelen. A rendszer üzenete: {}", fileArchive.getFileName(), e.getMessage()); + throw e; } } } @@ -102,21 +104,28 @@ public class CopyForArchiveNEXIOMaterialsStep extends JobStep { if (transferredFileNames.contains(origFileName)) { logger.info(systemMarker, "A '{}' file archiválásra felkészítése egy másik story kapcsán már megtörtént, ezért csak metaadat archiválás szükséges.", origFileName); - transferChunk(videoFileName); + if (!demo) + transferChunk(videoFileName); } else { existingMediaId = ItemManagerExtensions.getExistingRundownMedia(manager, origFileName); if (existingMediaId == 0) { transferredFileNames.add(origFileName); - transferFile(origFileName + MXFEXT, videoFileName); + if (!demo) + transferFile(origFileName + MXFEXT, videoFileName); + logger.info(systemMarker, "A '{}' file archiválásra felkészítése sikeres volt.", origFileName); } else { + logger.info(systemMarker, "A '{}' file archiválása már megtörtént, ezért csak metaadat archiválás szükséges.", origFileName); - transferChunk(videoFileName); + if (!demo) + transferChunk(videoFileName); } } - BasicDBObject metadata = createMetadata(rundownArchive, storyArchive, fileArchive, existingMediaId); - transferMetadata(videoFileName, metadata); - createSourceKillDateFile(rundownArchive, origFileName); + if (!demo) { + BasicDBObject metadata = createMetadata(rundownArchive, storyArchive, fileArchive, existingMediaId); + transferMetadata(videoFileName, metadata); + createSourceKillDateFile(rundownArchive, origFileName); + } } private BasicDBObject createMetadata(RundownArchive rundownArchive, StoryArchive storyArchive, FileArchive fileArchive, long existingMediaId) { @@ -229,7 +238,8 @@ public class CopyForArchiveNEXIOMaterialsStep extends JobStep { private RundownArchive processRundow(DBObject r) throws Exception { BasicDBObject rundown = (BasicDBObject) r; long rundownID = rundown.getLong(ID); - logger.info("Processing rundown {} {}", rundownID, rundown.getString(IOctopusAPI.NAME)); + if (!demo) + logger.info("Processing rundown {} {}", rundownID, rundown.getString(IOctopusAPI.NAME)); List stories = octopusAPI.getRundownFullStories(rundownID); if (stories == null) return null; @@ -274,23 +284,27 @@ public class CopyForArchiveNEXIOMaterialsStep extends JobStep { String rundownName = NoSQLUtils.asString(rundown, IOctopusAPI.NAME); try { BasicDBObject currentRundownID = new BasicDBObject(IOctopusAPI.ID, rundownID); - if (archivedRundowns != null && archivedRundowns.contains(currentRundownID)) { + if (!demo && archivedRundowns != null && archivedRundowns.contains(currentRundownID)) { logger.info("Skipping archived rundown {} {}", rundownID, rundownName); continue; } RundownArchive rundownArchive = processRundow(r); if (rundownArchive == null || rundownArchive.isEmpty()) { - logger.info("Skipping rundown {} {}", rundownID, rundownName); + if (!demo) + logger.info("Skipping rundown {} {}", rundownID, rundownName); continue; } - logger.info("Saving rundown {} {}", rundownID, rundownName); + if (!demo) + logger.info("Saving rundown {} {}", rundownID, rundownName); copy(rundownArchive); - logger.info(systemMarker, "A '{}' tükör {}db bejátszójának archiválása sikeres volt", rundownArchive.getItemTitle(), - rundownArchive.getStoryArchives().size()); + if (!demo) + logger.info(systemMarker, "A '{}' tükör {}db bejátszójának archiválása sikeres volt", rundownArchive.getItemTitle(), + rundownArchive.getStoryArchives().size()); - db.getCollection(ARCHIVEDRUNDOWNS).save(currentRundownID); + if (!demo) + db.getCollection(ARCHIVEDRUNDOWNS).save(currentRundownID); } catch (Exception e) { logger.catching(e); logger.error(systemMarker, diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/ItemManagerExtensions.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/ItemManagerExtensions.java index dbda83b3..81f199c0 100644 --- a/server/user.jobengine.executors/src/user/jobengine/server/steps/ItemManagerExtensions.java +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/ItemManagerExtensions.java @@ -29,27 +29,28 @@ public class ItemManagerExtensions { public static long getExistingRundownMedia(IItemManager manager, String houseid) { final long[] result = { 0 }; - int indexOf = houseid.lastIndexOf("-"); - if (indexOf > 0) { - final String idToCheck = houseid.substring(0, houseid.lastIndexOf("-")); - MetadataType metadataType = MetadataTypeDetector.GuessMetadataType(idToCheck); - if (metadataType == MetadataType.OctopusPlaceholder) { - StringBuilder query = new StringBuilder(); - query.append("select mediaid, mediafilehouseid, filename"); - query.append(" "); - query.append(String.format("from vw_rundown_items where mediafilehouseid like '%s%%'", idToCheck)); - query.append(" "); - query.append("order by filename, mediaid"); - IResultSetConsumer consumer = rs -> { - String fileName = rs.getString("filename"); - if (idToCheck.equals(fileName)) { - result[0] = rs.getLong("mediaid"); - return false; - } else - return true; - }; - manager.executeQuery(query.toString(), consumer, null); - } + final String[] idToCheck = { houseid }; + int pos = houseid.lastIndexOf("-"); + //a hivas a CopyForArchiveNEXIOMaterialsStep-bol is johet, ott meg nincs idobelyegezve a nev! + if (pos > 0 && houseid.length() - pos > 4) + idToCheck[0] = houseid.substring(0, pos); + MetadataType metadataType = MetadataTypeDetector.GuessMetadataType(idToCheck[0]); + if (metadataType == MetadataType.OctopusPlaceholder) { + StringBuilder query = new StringBuilder(); + query.append("select mediaid, mediafilehouseid, filename"); + query.append(" "); + query.append(String.format("from vw_rundown_items where mediafilehouseid like '%s%%'", idToCheck[0])); + query.append(" "); + query.append("order by filename, mediaid"); + IResultSetConsumer consumer = rs -> { + String fileName = rs.getString("filename"); + if (idToCheck[0].equals(fileName)) { + result[0] = rs.getLong("mediaid"); + return false; + } else + return true; + }; + manager.executeQuery(query.toString(), consumer, null); } return result[0]; diff --git a/server/user.jobengine.osgi.server/test/user/jobengine/server/IT/Support.java b/server/user.jobengine.osgi.server/test/user/jobengine/server/IT/Support.java index d9b58311..d5408eb5 100644 --- a/server/user.jobengine.osgi.server/test/user/jobengine/server/IT/Support.java +++ b/server/user.jobengine.osgi.server/test/user/jobengine/server/IT/Support.java @@ -592,5 +592,4 @@ public class Support { String idToCheck = houseid.substring(0, houseid.lastIndexOf("-")); System.out.println(idToCheck); } - }