Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Editor: Add Undo/Redo and auto-save-to-temporary list.

Adds an undo/redo stack, and saves changes to a temporary list as you
work.

Key bindings will be modified in future commits, so they're commented
from Jim's patch.

Thanks to Jim Stichnoth for the wonderful new feature.
  • Loading branch information...
commit 0791662a7cb823f3e1a2f2f393e28ae582134beb 1 parent 28d371e
@sphery sphery authored
View
63 mythtv/libs/libmyth/programinfo.cpp
@@ -2695,18 +2695,67 @@ AutoExpireType ProgramInfo::QueryAutoExpire(void) const
return kDisableAutoExpire;
}
-void ProgramInfo::QueryCutList(frm_dir_map_t &delMap) const
+bool ProgramInfo::QueryCutList(frm_dir_map_t &delMap, bool loadAutoSave) const
{
- QueryMarkupMap(delMap, MARK_CUT_START);
- QueryMarkupMap(delMap, MARK_CUT_END, true);
+ frm_dir_map_t autosaveMap;
+ QueryMarkupMap(autosaveMap, MARK_TMP_CUT_START);
+ QueryMarkupMap(autosaveMap, MARK_TMP_CUT_END, true);
+ QueryMarkupMap(autosaveMap, MARK_PLACEHOLDER, true);
+ bool result = !autosaveMap.isEmpty();
+
+ if (loadAutoSave)
+ {
+ // Convert the temporary marks into regular marks.
+ delMap.clear();
+ frm_dir_map_t::const_iterator i = autosaveMap.constBegin();
+ for (; i != autosaveMap.constEnd(); ++i)
+ {
+ uint64_t frame = i.key();
+ MarkTypes mark = i.value();
+ if (mark == MARK_TMP_CUT_START)
+ mark = MARK_CUT_START;
+ else if (mark == MARK_TMP_CUT_END)
+ mark = MARK_CUT_END;
+ delMap[frame] = mark;
+ }
+ }
+ else
+ {
+ QueryMarkupMap(delMap, MARK_CUT_START);
+ QueryMarkupMap(delMap, MARK_CUT_END, true);
+ QueryMarkupMap(delMap, MARK_PLACEHOLDER, true);
+ }
+
+ return result;
}
-void ProgramInfo::SaveCutList(frm_dir_map_t &delMap) const
+void ProgramInfo::SaveCutList(frm_dir_map_t &delMap, bool isAutoSave) const
{
- ClearMarkupMap(MARK_CUT_START);
- ClearMarkupMap(MARK_CUT_END);
+ if (!isAutoSave)
+ {
+ ClearMarkupMap(MARK_CUT_START);
+ ClearMarkupMap(MARK_CUT_END);
+ }
ClearMarkupMap(MARK_PLACEHOLDER);
- SaveMarkupMap(delMap);
+ ClearMarkupMap(MARK_TMP_CUT_START);
+ ClearMarkupMap(MARK_TMP_CUT_END);
+
+ frm_dir_map_t tmpDelMap;
+ frm_dir_map_t::const_iterator i = delMap.constBegin();
+ for (; i != delMap.constEnd(); ++i)
+ {
+ uint64_t frame = i.key();
+ MarkTypes mark = i.value();
+ if (isAutoSave)
+ {
+ if (mark == MARK_CUT_START)
+ mark = MARK_TMP_CUT_START;
+ else if (mark == MARK_CUT_END)
+ mark = MARK_TMP_CUT_END;
+ }
+ tmpDelMap[frame] = mark;
+ }
+ SaveMarkupMap(tmpDelMap);
if (IsRecording())
{
View
4 mythtv/libs/libmyth/programinfo.h
@@ -534,8 +534,8 @@ class MPUBLIC ProgramInfo
bool forceCheckLocal = false) const;
// Edit flagging map
- void QueryCutList(frm_dir_map_t &) const;
- void SaveCutList(frm_dir_map_t &) const;
+ bool QueryCutList(frm_dir_map_t &, bool loadAutosave=false) const;
+ void SaveCutList(frm_dir_map_t &, bool isAutoSave=false) const;
// Commercial flagging map
void QueryCommBreakList(frm_dir_map_t &) const;
View
2  mythtv/libs/libmyth/programtypes.cpp
@@ -22,6 +22,8 @@ QString toString(MarkTypes type)
switch (type)
{
case MARK_UNSET: return "UNSET";
+ case MARK_TMP_CUT_END: return "TMP_CUT_END";
+ case MARK_TMP_CUT_START:return "TMP_CUT_START";
case MARK_UPDATED_CUT: return "UPDATED_CUT";
case MARK_PLACEHOLDER: return "PLACEHOLDER";
case MARK_CUT_END: return "CUT_END";
View
2  mythtv/libs/libmyth/programtypes.h
@@ -40,6 +40,8 @@ typedef QMap<uint64_t, uint64_t> frm_pos_map_t;
typedef enum {
MARK_ALL = -100,
MARK_UNSET = -10,
+ MARK_TMP_CUT_END = -5,
+ MARK_TMP_CUT_START = -4,
MARK_UPDATED_CUT = -3,
MARK_PLACEHOLDER = -2,
MARK_CUT_END = 0,
View
127 mythtv/libs/libmythtv/deletemap.cpp
@@ -10,6 +10,61 @@
#define EDIT_CHECK if(!m_editing) \
{ VERBOSE(VB_IMPORTANT, LOC_ERR + "Cannot edit outside editmode."); return; }
+DeleteMapUndoEntry::DeleteMapUndoEntry(frm_dir_map_t dm, QString msg) :
+ deleteMap(dm), message(msg) { }
+
+DeleteMapUndoEntry::DeleteMapUndoEntry(void)
+{
+ frm_dir_map_t dm;
+ deleteMap = dm;
+ message = "";
+}
+
+void DeleteMap::Push(QString undoMessage)
+{
+ DeleteMapUndoEntry entry(m_deleteMap, undoMessage);
+ // Remove all "redo" entries
+ while (m_undoStack.size() > m_undoStackPointer + 1)
+ m_undoStack.pop_back();
+ m_undoStack.append(entry);
+ m_undoStackPointer ++;
+ SaveMap(0, m_ctx, true);
+}
+
+bool DeleteMap::Undo(void)
+{
+ if (!HasUndo())
+ return false;
+ m_undoStackPointer --;
+ m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
+ m_changed = true;
+ SaveMap(0, m_ctx, true);
+ return true;
+}
+
+bool DeleteMap::Redo(void)
+{
+ if (!HasRedo())
+ return false;
+ m_undoStackPointer ++;
+ m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
+ m_changed = true;
+ SaveMap(0, m_ctx, true);
+ return true;
+}
+
+QString DeleteMap::GetUndoMessage(void)
+{
+ return (HasUndo() ? m_undoStack[m_undoStackPointer].message :
+ QObject::tr("(No more undo operations)"));
+}
+
+QString DeleteMap::GetRedoMessage(void)
+{
+ return (HasRedo() ? m_undoStack[m_undoStackPointer + 1].message :
+ QObject::tr("(No more redo operations)"));
+}
+
bool DeleteMap::HandleAction(QString &action, uint64_t frame,
uint64_t played, uint64_t total, double rate)
{
@@ -21,7 +76,7 @@ bool DeleteMap::HandleAction(QString &action, uint64_t frame,
else if (action == ACTION_DOWN)
UpdateSeekAmount(-1, rate);
else if (action == ACTION_CLEARMAP)
- Clear();
+ Clear(QObject::tr("Clear Cut List"));
else if (action == ACTION_INVERTMAP)
ReverseAll(total);
else if (action == "MOVEPREV")
@@ -29,13 +84,17 @@ bool DeleteMap::HandleAction(QString &action, uint64_t frame,
else if (action == "MOVENEXT")
MoveRelative(frame, total, true);
else if (action == "CUTTOBEGINNING")
- Add(frame, total, MARK_CUT_END);
+ Add(frame, total, MARK_CUT_END, QObject::tr("Cut To Beginning"));
else if (action == "CUTTOEND")
- Add(frame, total, MARK_CUT_START);
+ Add(frame, total, MARK_CUT_START, QObject::tr("Cut To End"));
else if (action == "NEWCUT")
NewCut(frame, total);
else if (action == "DELETE")
- Delete(frame, total);
+ Delete(frame, total, QObject::tr("Delete Cut Area"));
+ else if (action == "UNDO")
+ Undo();
+ else if (action == "REDO")
+ Redo();
else
handled = false;
return handled;
@@ -150,10 +209,12 @@ bool DeleteMap::IsEmpty(void)
}
/// Clears the deleteMap.
-void DeleteMap::Clear(void)
+void DeleteMap::Clear(QString undoMessage)
{
m_deleteMap.clear();
m_changed = true;
+ if (!undoMessage.isEmpty())
+ Push(undoMessage);
}
/// Reverses the direction of each mark in the map.
@@ -165,6 +226,7 @@ void DeleteMap::ReverseAll(uint64_t total)
Add(it.key(), it.value() == MARK_CUT_END ? MARK_CUT_START :
MARK_CUT_END);
CleanMap(total);
+ Push(QObject::tr("Invert Cut List"));
}
/**
@@ -172,7 +234,8 @@ void DeleteMap::ReverseAll(uint64_t total)
* existing redundant mark of that type is removed. This simplifies
* the cleanup code.
*/
-void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type)
+void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type,
+ QString undoMessage)
{
EDIT_CHECK
if ((MARK_CUT_START != type) && (MARK_CUT_END != type) &&
@@ -186,7 +249,7 @@ void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type)
{
// Delete the temporary mark before putting a real mark at its
// location
- Delete(frame, total);
+ Delete(frame, total, "");
}
else // Don't add a mark on top of a mark
return;
@@ -241,10 +304,12 @@ void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type)
Delete((uint64_t)remove);
Add(frame, type);
CleanMap(total);
+ if (!undoMessage.isEmpty())
+ Push(undoMessage);
}
/// Remove the mark at the given frame.
-void DeleteMap::Delete(uint64_t frame, uint64_t total)
+void DeleteMap::Delete(uint64_t frame, uint64_t total, QString undoMessage)
{
EDIT_CHECK
if (m_deleteMap.isEmpty())
@@ -271,6 +336,8 @@ void DeleteMap::Delete(uint64_t frame, uint64_t total)
if (prev != next)
Delete(next);
CleanMap(total);
+ if (!undoMessage.isEmpty())
+ Push(undoMessage);
}
/// Reverse the direction of the mark at the given frame.
@@ -278,7 +345,8 @@ void DeleteMap::Reverse(uint64_t frame, uint64_t total)
{
EDIT_CHECK
int type = Delete(frame);
- Add(frame, total, type == MARK_CUT_END ? MARK_CUT_START : MARK_CUT_END);
+ Add(frame, total, type == MARK_CUT_END ? MARK_CUT_START : MARK_CUT_END, "");
+ Push(QObject::tr("Reverse Mark Direction"));
}
/// Add a new cut marker (to start or end a cut region)
@@ -376,6 +444,7 @@ void DeleteMap::NewCut(uint64_t frame, uint64_t total)
Add(frame, MARK_PLACEHOLDER);
CleanMap(total);
+ Push(QObject::tr("New Cut"));
}
/// Move the previous (!right) or next (right) cut to frame.
@@ -397,14 +466,14 @@ void DeleteMap::MoveRelative(uint64_t frame, uint64_t total, bool right)
{
// If on a mark, don't collapse a cut region to 0;
// instead, delete the region
- Delete(frame, total);
+ Delete(frame, total, QObject::tr("Delete Cut Area"));
return;
}
else if (MARK_PLACEHOLDER == type)
{
// Delete the temporary mark before putting a real mark at its
// location
- Delete(frame, total);
+ Delete(frame, total, "");
}
}
@@ -424,7 +493,7 @@ void DeleteMap::Move(uint64_t frame, uint64_t to, uint64_t total)
else if (frame == total)
type = MARK_CUT_END;
}
- Add(to, total, type);
+ Add(to, total, type, QObject::tr("Move Mark"));
}
/// Private addition to the deleteMap.
@@ -586,6 +655,7 @@ void DeleteMap::SetMap(const frm_dir_map_t &map)
Clear();
m_deleteMap = map;
m_deleteMap.detach();
+ Push(QObject::tr("Set New Cut List"));
}
/// Loads the given commercial break map into the deleteMap.
@@ -597,10 +667,11 @@ void DeleteMap::LoadCommBreakMap(uint64_t total, frm_dir_map_t &map)
Add(it.key(), it.value() == MARK_COMM_START ?
MARK_CUT_START : MARK_CUT_END);
CleanMap(total);
+ Push(QObject::tr("Load Commskip List"));
}
/// Loads the delete map from the database.
-void DeleteMap::LoadMap(uint64_t total, PlayerContext *ctx)
+void DeleteMap::LoadMap(uint64_t total, PlayerContext *ctx, QString undoMessage)
{
if (!ctx || !ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
return;
@@ -610,14 +681,39 @@ void DeleteMap::LoadMap(uint64_t total, PlayerContext *ctx)
ctx->playingInfo->QueryCutList(m_deleteMap);
ctx->UnlockPlayingInfo(__FILE__, __LINE__);
CleanMap(total);
+ if (!undoMessage.isEmpty())
+ Push(undoMessage);
+}
+
+/// Returns true if an auto-save map was loaded.
+/// Does nothing and returns false if not.
+bool DeleteMap::LoadAutoSaveMap(uint64_t total, PlayerContext *ctx)
+{
+ if (!ctx || !ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
+ return false;
+
+ frm_dir_map_t tmpDeleteMap = m_deleteMap;
+ Clear();
+ ctx->LockPlayingInfo(__FILE__, __LINE__);
+ bool result = ctx->playingInfo->QueryCutList(m_deleteMap, true);
+ ctx->UnlockPlayingInfo(__FILE__, __LINE__);
+ CleanMap(total);
+ if (result)
+ Push(QObject::tr("Load Auto-Save Cut List"));
+ else
+ m_deleteMap = tmpDeleteMap;
+
+ return result;
}
/// Saves the delete map to the database.
-void DeleteMap::SaveMap(uint64_t total, PlayerContext *ctx)
+void DeleteMap::SaveMap(uint64_t total, PlayerContext *ctx, bool isAutoSave)
{
if (!ctx || !ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
return;
+ if (!isAutoSave)
+ {
// Remove temporary placeholder marks
QMutableMapIterator<uint64_t, MarkTypes> it(m_deleteMap);
while (it.hasNext())
@@ -631,9 +727,10 @@ void DeleteMap::SaveMap(uint64_t total, PlayerContext *ctx)
}
CleanMap(total);
+ }
ctx->LockPlayingInfo(__FILE__, __LINE__);
ctx->playingInfo->SaveMarkupFlag(MARK_UPDATED_CUT);
- ctx->playingInfo->SaveCutList(m_deleteMap);
+ ctx->playingInfo->SaveCutList(m_deleteMap, isAutoSave);
ctx->UnlockPlayingInfo(__FILE__, __LINE__);
}
View
40 mythtv/libs/libmythtv/deletemap.h
@@ -4,12 +4,24 @@
#include "programinfo.h"
#include "playercontext.h"
+class DeleteMap;
+
+typedef struct DeleteMapUndoEntry
+{
+ frm_dir_map_t deleteMap;
+ QString message; // how we got from previous map to this map
+ DeleteMapUndoEntry(frm_dir_map_t dm, QString msg);
+ DeleteMapUndoEntry(void);
+} DeleteMapUndoEntry;
+
class DeleteMap
{
public:
DeleteMap(): m_editing(false), m_nextCutStart(0), m_changed(true),
- m_seekamountpos(4), m_seekamount(30) { }
+ m_seekamountpos(4), m_seekamount(30),
+ m_ctx(0), m_undoStackPointer(-1) { Push(""); }
+ void SetPlayerContext(PlayerContext *ctx) { m_ctx = ctx; }
bool HandleAction(QString &action, uint64_t frame, uint64_t played,
uint64_t total, double rate);
int GetSeekAmount(void) { return m_seekamount; }
@@ -28,15 +40,17 @@ class DeleteMap
void SetMap(const frm_dir_map_t &map);
void LoadCommBreakMap(uint64_t total, frm_dir_map_t &map);
- void SaveMap(uint64_t total, PlayerContext *ctx);
- void LoadMap(uint64_t total, PlayerContext *ctx);
+ void SaveMap(uint64_t total, PlayerContext *ctx, bool isAutoSave=false);
+ void LoadMap(uint64_t total, PlayerContext *ctx, QString undoMessage="");
+ bool LoadAutoSaveMap(uint64_t total, PlayerContext *ctx);
void CleanMap(uint64_t total);
- void Clear(void);
+ void Clear(QString undoMessage="");
void ReverseAll(uint64_t total);
- void Add(uint64_t frame, uint64_t total, MarkTypes type);
+ void Add(uint64_t frame, uint64_t total, MarkTypes type,
+ QString undoMessage);
void NewCut(uint64_t frame, uint64_t total);
- void Delete(uint64_t frame, uint64_t total);
+ void Delete(uint64_t frame, uint64_t total, QString undoMessage);
void Reverse(uint64_t frame, uint64_t total);
void MoveRelative(uint64_t frame, uint64_t total, bool right);
void Move(uint64_t frame, uint64_t to, uint64_t total);
@@ -50,10 +64,19 @@ class DeleteMap
void TrackerReset(uint64_t frame, uint64_t total);
bool TrackerWantsToJump(uint64_t frame, uint64_t total, uint64_t &to);
+ bool Undo(void);
+ bool Redo(void);
+ bool HasUndo(void) { return m_undoStackPointer > 0; }
+ bool HasRedo(void) { return m_undoStackPointer < m_undoStack.size() - 1; }
+ QString GetUndoMessage(void);
+ QString GetRedoMessage(void);
+
private:
void Add(uint64_t frame, MarkTypes type);
MarkTypes Delete(uint64_t frame);
+ void Push(QString undoMessage);
+
bool m_editing;
uint64_t m_nextCutStart;
frm_dir_map_t m_deleteMap;
@@ -61,6 +84,11 @@ class DeleteMap
bool m_changed;
int m_seekamountpos;
int m_seekamount;
+ PlayerContext *m_ctx;
+
+ // Invariant: m_undoStack[m_undoStackPointer].deleteMap == m_deleteMap
+ QVector<DeleteMapUndoEntry> m_undoStack;
+ int m_undoStackPointer;
};
#endif // DELETEMAP_H
View
33 mythtv/libs/libmythtv/mythplayer.cpp
@@ -3529,6 +3529,7 @@ void MythPlayer::ClearAfterSeek(bool clearvideobuffers)
void MythPlayer::SetPlayerInfo(TV *tv, QWidget *widget,
bool frame_exact_seek, PlayerContext *ctx)
{
+ deleteMap.SetPlayerContext(ctx);
m_tv = tv;
parentWidget = widget;
exactseeks = frame_exact_seek;
@@ -3560,6 +3561,14 @@ bool MythPlayer::EnableEdit(void)
osd->DialogQuit();
ResetCaptions();
osd->HideAll();
+
+ bool loadedAutoSave = deleteMap.LoadAutoSaveMap(totalFrames, player_ctx);
+ if (loadedAutoSave)
+ {
+ SetOSDMessage(QObject::tr("Using previously auto-saved cut list"),
+ kOSDTimeout_Short);
+ }
+
deleteMap.UpdateSeekAmount(0, video_frame_rate);
deleteMap.UpdateOSD(framesPlayed, totalFrames, video_frame_rate,
player_ctx, osd);
@@ -3580,10 +3589,10 @@ void MythPlayer::DisableEdit(bool save)
return;
deleteMap.SetEditing(false, osd);
- if (save)
- deleteMap.SaveMap(totalFrames, player_ctx);
- else
+ if (!save)
deleteMap.LoadMap(totalFrames, player_ctx);
+ // Unconditionally save to remove temporary marks from the DB.
+ deleteMap.SaveMap(totalFrames, player_ctx);
deleteMap.TrackerReset(framesPlayed, totalFrames);
deleteMap.SetFileEditing(player_ctx, false);
player_ctx->LockPlayingInfo(__FILE__, __LINE__);
@@ -3678,12 +3687,14 @@ bool MythPlayer::HandleProgramEditorActions(QStringList &actions,
}
else if (action == "DELETE")
{
- deleteMap.Delete(frame, totalFrames);
+ deleteMap.Delete(frame, totalFrames,
+ QObject::tr("Delete cut area"));
refresh = true;
}
else if (action == "REVERT")
{
- deleteMap.LoadMap(totalFrames, player_ctx);
+ deleteMap.LoadMap(totalFrames, player_ctx,
+ QObject::tr("Load Cut List"));
refresh = true;
}
else if (action == "REVERTEXIT")
@@ -3703,6 +3714,8 @@ bool MythPlayer::HandleProgramEditorActions(QStringList &actions,
}
else
{
+ QString undoMessage = deleteMap.GetUndoMessage();
+ QString redoMessage = deleteMap.GetRedoMessage();
handled = deleteMap.HandleAction(action, frame, framesPlayed,
totalFrames, video_frame_rate);
if (handled && (action == "CUTTOBEGINNING" ||
@@ -3710,6 +3723,16 @@ bool MythPlayer::HandleProgramEditorActions(QStringList &actions,
{
SetOSDMessage(QObject::tr("New cut added."), kOSDTimeout_Short);
}
+ else if (handled && action == "UNDO")
+ {
+ SetOSDMessage(QObject::tr("Undo") + " - " + undoMessage,
+ kOSDTimeout_Short);
+ }
+ else if (handled && action == "REDO")
+ {
+ SetOSDMessage(QObject::tr("Redo") + " - " + redoMessage,
+ kOSDTimeout_Short);
+ }
}
}
View
4 mythtv/libs/libmythtv/mythplayer.h
@@ -397,6 +397,10 @@ class MPUBLIC MythPlayer
bool IsTemporaryMark(uint64_t frame);
bool HasTemporaryMark(void);
bool IsCutListSaved(PlayerContext *ctx) { return deleteMap.IsSaved(ctx); }
+ bool DeleteMapHasUndo(void) { return deleteMap.HasUndo(); }
+ bool DeleteMapHasRedo(void) { return deleteMap.HasRedo(); }
+ QString DeleteMapGetUndoMessage(void) { return deleteMap.GetUndoMessage(); }
+ QString DeleteMapGetRedoMessage(void) { return deleteMap.GetRedoMessage(); }
// Reinit
void ReinitOSD(void);
View
12 mythtv/libs/libmythtv/tv_play.cpp
@@ -723,6 +723,10 @@ void TV::InitKeys(void)
"Jump back 10x the normal amount"), ",,<");
REG_KEY("TV Editing", ACTION_BIGJUMPFWD, QT_TRANSLATE_NOOP("MythControls",
"Jump forward 10x the normal amount"), ">,.");
+// REG_KEY("TV Editing", "UNDO", QT_TRANSLATE_NOOP("MythControls",
+// "Undo"), "Ctrl+Z");
+// REG_KEY("TV Editing", "REDO", QT_TRANSLATE_NOOP("MythControls",
+// "Redo"), "Ctrl+Y");
/* Teletext keys */
REG_KEY("Teletext Menu", ACTION_NEXTPAGE, QT_TRANSLATE_NOOP("MythControls",
@@ -8915,6 +8919,14 @@ void TV::ShowOSDCutpoint(PlayerContext *ctx, const QString &type)
if ("EDIT_CUT_POINTS" == type)
osd->DialogAddButton(QObject::tr("Cut List Options"),
"DIALOG_CUTPOINT_CUTLISTOPTIONS_0", true);
+ if (ctx->player->DeleteMapHasUndo())
+ osd->DialogAddButton(QObject::tr("Undo") + " - " +
+ ctx->player->DeleteMapGetUndoMessage(),
+ QString("DIALOG_CUTPOINT_UNDO_0"));
+ if (ctx->player->DeleteMapHasRedo())
+ osd->DialogAddButton(QObject::tr("Redo") + " - " +
+ ctx->player->DeleteMapGetRedoMessage(),
+ QString("DIALOG_CUTPOINT_REDO_0"));
}
else if ("CUT_LIST_OPTIONS" == type)
{
Please sign in to comment.
Something went wrong with that request. Please try again.