diff --git a/resources/scripts/scrapers/youtube-limit-length.py b/resources/scripts/scrapers/youtube-limit-length.py new file mode 100644 index 000000000..f1595ffb3 --- /dev/null +++ b/resources/scripts/scrapers/youtube-limit-length.py @@ -0,0 +1,46 @@ +# Use this as RSS Guard post-processing script. +# +# Example command line usage: +# curl 'PATH_TO_SOME_YOUTUBE_CHANNEL_RSS_FEED' | python 'youtube-limit-length.py' + +import sys +import xml.etree.ElementTree as ET +import requests +import json +import isodate + +sys.stdin.reconfigure(encoding="utf-8") +input_data = sys.stdin.read() + +api_key = "YOUR_API_KEY_HERE" +min_required_length = 90 # In seconds. + +youtube_query = ( + "https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=VIDEO-ID&key=" + + api_key +) +ns = { + "atom": "http://www.w3.org/2005/Atom", + "yt": "http://www.youtube.com/xml/schemas/2015", +} + +tree = ET.fromstring(input_data) +entries = tree.findall("atom:entry", ns) + +for entry in entries: + video_id = entry.find("yt:videoId", ns).text + youtube_query_specific = youtube_query.replace("VIDEO-ID", video_id) + api_response = requests.get(youtube_query_specific).text + response_json = json.loads(api_response) + + try: + length_encoded = str(response_json["items"][0]["contentDetails"]["duration"]) + except: + continue + + length = isodate.parse_duration(length_encoded) + + if length.seconds < min_required_length: + tree.remove(entry) + +sys.stdout.buffer.write(ET.tostring(tree, encoding='utf-8', method='xml')) \ No newline at end of file diff --git a/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.cpp b/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.cpp index 0a942bf8c..e2ea44091 100644 --- a/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.cpp +++ b/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.cpp @@ -4,6 +4,7 @@ #include "definitions/definitions.h" #include "dynamic-shortcuts/shortcutcatcher.h" +#include "gui/messagebox.h" #include #include @@ -25,15 +26,15 @@ bool DynamicShortcutsWidget::areShortcutsUnique() const { QList all_shortcuts; // Obtain all shortcuts. - for (const ActionBinding& binding : m_actionBindings) { - const QKeySequence new_shortcut = binding.second->shortcut(); + for (ShortcutCatcher* catcher : m_actionBindings) { + const QKeySequence new_shortcut = catcher->shortcut(); if (!new_shortcut.isEmpty() && all_shortcuts.contains(new_shortcut)) { // Problem, two identical non-empty shortcuts found. return false; } else { - all_shortcuts.append(binding.second->shortcut()); + all_shortcuts.append(catcher->shortcut()); } } @@ -41,8 +42,8 @@ bool DynamicShortcutsWidget::areShortcutsUnique() const { } void DynamicShortcutsWidget::updateShortcuts() { - for (const ActionBinding& binding : std::as_const(m_actionBindings)) { - binding.first->setShortcut(binding.second->shortcut()); + for (ShortcutCatcher* catcher : std::as_const(m_actionBindings)) { + catcher->action()->setShortcut(catcher->shortcut()); } } @@ -58,15 +59,14 @@ void DynamicShortcutsWidget::populate(QList actions) { // Create shortcut catcher for this action and set default shortcut. auto* catcher = new ShortcutCatcher(this); + catcher->setAction(action); catcher->setDefaultShortcut(action->shortcut()); - // Store information for re-initialization of shortcuts - // of actions when widget gets "confirmed". - QPair new_binding; + if (!action->shortcut().isEmpty()) { + m_assignedShortcuts.insert(action->shortcut(), catcher); + } - new_binding.first = action; - new_binding.second = catcher; - m_actionBindings << new_binding; + m_actionBindings.append(catcher); // Add new catcher to our control. auto* action_label = new QLabel(this); @@ -89,15 +89,63 @@ void DynamicShortcutsWidget::populate(QList actions) { action_icon->setPixmap(action->icon().pixmap(ICON_SIZE_SETTINGS, ICON_SIZE_SETTINGS)); action_icon->setToolTip(action->toolTip()); + m_layout->addWidget(action_icon, row_id, 0); m_layout->addWidget(action_label, row_id, 1); m_layout->addWidget(catcher, row_id, 2); row_id++; - connect(catcher, &ShortcutCatcher::shortcutChanged, this, &DynamicShortcutsWidget::setupChanged); + connect(catcher, &ShortcutCatcher::shortcutChanged, this, &DynamicShortcutsWidget::onShortcutChanged); } // Make sure that "spacer" is added. m_layout->setRowStretch(row_id, 1); m_layout->setColumnStretch(1, 1); } + +void DynamicShortcutsWidget::onShortcutChanged(const QKeySequence& sequence) { + ShortcutCatcher* catcher = qobject_cast(sender()); + QKeySequence assigned_sequence = m_assignedShortcuts.key(catcher); + + qDebugNN << catcher->action()->text(); + + // We remove currently assigned sequence to this catcher. + m_assignedShortcuts.remove(assigned_sequence); + + if (!sequence.isEmpty()) { + // Now we check if any other catcher has assigned the same sequence. + ShortcutCatcher* conflicting_catcher = m_assignedShortcuts.value(sequence); + + if (conflicting_catcher != nullptr) { + // We found conflicting catcher. + catcher->blockSignals(true); + + QMessageBox::StandardButton decision = + MsgBox::show(this, + QMessageBox::Icon::Critical, + tr("Duplicate shortcut"), + tr("There is another action which has the same shortcut assigned."), + tr("Do you want to keep the new shortcut assignment and clear the previous shortcut?"), + conflicting_catcher->action()->text().remove(QSL("&")), + QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, + QMessageBox::StandardButton::Yes); + + catcher->blockSignals(false); + + if (decision == QMessageBox::StandardButton::Yes) { + // We keep the new shortcut and reset old conflicting action. + m_assignedShortcuts.insert(sequence, catcher); + conflicting_catcher->clearShortcut(); + } + else { + // We discard new shortcut. + catcher->clearShortcut(); + } + } + else { + m_assignedShortcuts.insert(sequence, catcher); + } + } + + emit setupChanged(); +} diff --git a/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.h b/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.h index d1ade2e86..5d6b55a4b 100644 --- a/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.h +++ b/src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.h @@ -8,8 +8,6 @@ class QGridLayout; class ShortcutCatcher; -typedef QPair ActionBinding; - class DynamicShortcutsWidget : public QWidget { Q_OBJECT @@ -33,12 +31,16 @@ class DynamicShortcutsWidget : public QWidget { // assigned to actions before calling this method. void populate(QList actions); + private slots: + void onShortcutChanged(const QKeySequence& sequence); + signals: void setupChanged(); private: QGridLayout* m_layout; - QList m_actionBindings; + QList m_actionBindings; + QHash m_assignedShortcuts; }; #endif // DYNAMICSHORTCUTSOVERVIEW_H diff --git a/src/librssguard/dynamic-shortcuts/shortcutcatcher.cpp b/src/librssguard/dynamic-shortcuts/shortcutcatcher.cpp index 45008946b..d020ef644 100644 --- a/src/librssguard/dynamic-shortcuts/shortcutcatcher.cpp +++ b/src/librssguard/dynamic-shortcuts/shortcutcatcher.cpp @@ -65,3 +65,11 @@ void ShortcutCatcher::resetShortcut() { void ShortcutCatcher::clearShortcut() { setShortcut(QKeySequence()); } + +QAction* ShortcutCatcher::action() const { + return m_action; +} + +void ShortcutCatcher::setAction(QAction* act) { + m_action = act; +} diff --git a/src/librssguard/dynamic-shortcuts/shortcutcatcher.h b/src/librssguard/dynamic-shortcuts/shortcutcatcher.h index 4c7222927..6ae7bc32c 100644 --- a/src/librssguard/dynamic-shortcuts/shortcutcatcher.h +++ b/src/librssguard/dynamic-shortcuts/shortcutcatcher.h @@ -19,6 +19,9 @@ class ShortcutCatcher : public QWidget { void setDefaultShortcut(const QKeySequence& key); void setShortcut(const QKeySequence& key); + QAction* action() const; + void setAction(QAction* act); + public slots: void resetShortcut(); void clearShortcut(); @@ -27,6 +30,7 @@ class ShortcutCatcher : public QWidget { void shortcutChanged(const QKeySequence& seguence); private: + QAction* m_action; PlainToolButton* m_btnReset; PlainToolButton* m_btnClear; QKeySequenceEdit* m_shortcutBox;