source: smplayer/trunk/src/playlist.cpp@ 188

Last change on this file since 188 was 188, checked in by Silvan Scherrer, 9 years ago

SMPlayer: update trunk to version 17.1.0

  • Property svn:eol-style set to LF
File size: 65.6 KB
RevLine 
[112]1/* smplayer, GUI front-end for mplayer.
[188]2 Copyright (C) 2006-2017 Ricardo Villalba <[email protected]>
[112]3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17*/
18
19#include "playlist.h"
20
[181]21#include <QTableView>
22#include <QStandardItemModel>
23#include <QSortFilterProxyModel>
24#include <QStyledItemDelegate>
[112]25#include <QToolBar>
26#include <QFile>
27#include <QTextStream>
28#include <QDir>
29#include <QFileInfo>
30#include <QMessageBox>
31#include <QPushButton>
32#include <QMenu>
33#include <QDateTime>
34#include <QSettings>
35#include <QInputDialog>
36#include <QToolButton>
37#include <QTimer>
38#include <QVBoxLayout>
39#include <QUrl>
40#include <QDragEnterEvent>
41#include <QDropEvent>
42#include <QHeaderView>
43#include <QTextCodec>
44#include <QApplication>
[165]45#include <QMimeData>
[176]46#include <QDomDocument>
[188]47#include <QDesktopServices>
[176]48#include <QDebug>
[112]49
[181]50#if QT_VERSION >= 0x050000
51#include "myscroller.h"
52#endif
53
[112]54#include "myaction.h"
[181]55#include "mylineedit.h"
[112]56#include "filedialog.h"
57#include "helper.h"
58#include "images.h"
59#include "preferences.h"
[142]60#include "multilineinputdialog.h"
[112]61#include "version.h"
62#include "extensions.h"
63#include "guiconfig.h"
64
[188]65#ifdef CHROMECAST_SUPPORT
66#include "chromecast.h"
67#endif
68
[181]69#ifdef PLAYLIST_DOWNLOAD
70#include "inputurl.h"
71#include "youtube/loadpage.h"
72#include "urlhistory.h"
73#include <QNetworkAccessManager>
74#include <QTemporaryFile>
75#include <QClipboard>
76#include <QMovie>
77#endif
[112]78
79#if USE_INFOPROVIDER
80#include "infoprovider.h"
81#endif
82
83#define DRAG_ITEMS 0
[176]84#define PL_ALLOW_DUPLICATES 1
[181]85#define SIMULATE_FILE_DELETION 0
86#define USE_ITEM_DELEGATE 0
[112]87
[181]88#define COL_NUM 0
[112]89#define COL_NAME 1
90#define COL_TIME 2
[181]91#define COL_FILENAME 3
[112]92
[181]93#if USE_ITEM_DELEGATE
94class PlaylistDelegate : public QStyledItemDelegate {
95public:
96 PlaylistDelegate(QObject * parent = 0) : QStyledItemDelegate(parent) {};
97 virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
98 QStyleOptionViewItem opt = option;
99 initStyleOption(&opt, index);
100 if (index.column() == COL_NAME) {
101 bool played = index.data(PLItem::Role_Played).toBool();
102 bool current = index.data(PLItem::Role_Current).toBool();
103 if (current) opt.font.setBold(true);
104 else
105 if (played) opt.font.setItalic(true);
106 }
107 else
108 if (index.column() == COL_FILENAME) {
109 opt.textElideMode = Qt::ElideMiddle;
110 }
111 QStyledItemDelegate::paint(painter, opt, index);
112 }
113};
114#endif
[112]115
[181]116/* ----------------------------------------------------------- */
[112]117
118
[181]119PLItem::PLItem() : QStandardItem() {
120 col_num = new QStandardItem();
121 col_duration = new QStandardItem();
122 col_filename = new QStandardItem();
[112]123
[181]124 setDuration(0);
125 setPlayed(false);
126 setCurrent(false);
[112]127
[181]128 col_num->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
129}
[112]130
[181]131PLItem::PLItem(const QString filename, const QString name, double duration) : QStandardItem() {
132 col_num = new QStandardItem();
133 col_duration = new QStandardItem();
134 col_filename = new QStandardItem();
[112]135
[181]136 setFilename(filename);
137 setName(name);
138 setDuration(duration);
139 setPlayed(false);
140 setCurrent(false);
141
142 col_num->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
143}
144
145PLItem::~PLItem() {
146}
147
148void PLItem::setFilename(const QString filename) {
149 col_filename->setText(filename);
150 col_filename->setToolTip(filename);
151 col_filename->setData(filename);
152
153 if (!filename.contains("://") && filename.count() > 50) {
154 QStringList parts = filename.split(QDir::separator());
155 //if (!parts.isEmpty() && parts[0].isEmpty()) parts[0] = "/";
156 //qDebug() << "PLItem::setFilename: parts count:" << parts.count() << "parts:" << parts;
157 if (parts.count() >= 2) {
158 QString s = parts[parts.count()-2] + QDir::separator() + parts[parts.count()-1];
159 if (parts.count() > 2) s = QString("...") + QDir::separator() + s;
160 col_filename->setText(s);
161 }
162 }
163}
164
165void PLItem::setName(const QString name) {
166 setText(name);
167 setData(name);
168 setToolTip(name);
169}
170
171void PLItem::setDuration(double duration) {
172 col_duration->setData(duration);
173 col_duration->setText(Helper::formatTime(duration));
174}
175
176void PLItem::setPlayed(bool played) {
177 setData(played, Role_Played);
178#if !USE_ITEM_DELEGATE
179 QFont f = font();
180 f.setItalic(played);
181 setFont(f);
182#endif
183}
184
185void PLItem::setPosition(int position) {
186 //col_num->setText(QString("%1").arg(position, 4, 10, QChar('0')));
187 col_num->setText(QString::number(position));
188 col_num->setData(position);
189}
190
191void PLItem::setCurrent(bool b) {
192 setData(b, Role_Current);
193#if !USE_ITEM_DELEGATE
194 QFont f = font();
195 f.setBold(b);
196 f.setItalic(b ? false : played());
197 setFont(f);
198#endif
199}
200
201QString PLItem::filename() {
202 return col_filename->data().toString();
203}
204
205QString PLItem::name() {
206 return text();
207}
208
209double PLItem::duration() {
210 return col_duration->data().toDouble();
211}
212
213bool PLItem::played() {
214 return data(Role_Played).toBool();
215}
216
217int PLItem::position() {
218 return col_num->data().toInt();
219}
220
221bool PLItem::isCurrent() {
222 return data(Role_Current).toBool();
223}
224
225QList<QStandardItem *> PLItem::items() {
226 QList<QStandardItem *> l;
227 l << col_num << this << col_duration << col_filename;
228 return l;
229}
230
[188]231void PLItem::setExtraParams(const QStringList & pars) {
232 setData(pars, Role_Params);
233}
[181]234
[188]235QStringList PLItem::extraParams() {
236 return data(Role_Params).toStringList();
237}
238
239void PLItem::setVideoURL(const QString & url) {
240 setData(url, Role_Video_URL);
241}
242
243QString PLItem::videoURL() {
244 return data(Role_Video_URL).toString();
245}
246
[181]247/* ----------------------------------------------------------- */
248
249
250Playlist::Playlist(QWidget * parent, Qt::WindowFlags f)
251 : QWidget(parent,f)
252 , set(0)
253 , modified(false)
254 , recursive_add_directory(false)
255 , automatically_get_info(false)
256 , save_playlist_in_config(true)
257 , play_files_from_start(true)
258 , row_spacing(-1) // Default height
[188]259 , start_play_on_load(true)
[181]260 , automatically_play_next(true)
261 , ignore_player_errors(false)
262 , change_name(true)
263 , save_dirs(true)
264{
265 playlist_path = "";
266 latest_dir = "";
267
[188]268 filter_edit = new MyLineEdit(this);
269 connect(filter_edit, SIGNAL(textChanged(const QString &)), this, SLOT(filterEditChanged(const QString &)));
270
[112]271 createTable();
272 createActions();
273 createToolbar();
274
275 QVBoxLayout *layout = new QVBoxLayout;
[188]276#ifdef PLAYLIST_DOUBLE_TOOLBAR
277 layout->addWidget(toolbar);
[181]278 layout->addWidget(listView);
[188]279 layout->addWidget(toolbar2);
280#else
281 layout->addWidget(listView);
[181]282 layout->addWidget(toolbar);
[188]283 layout->addWidget(filter_edit);
284 filter_edit->hide();
[181]285#endif
[112]286 setLayout(layout);
287
[181]288 clear();
[112]289
290 retranslateStrings();
291
292#if !DOCK_PLAYLIST
293 setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding );
294 adjustSize();
295#else
296 //setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Expanding );
297 //setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
298 setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
299#endif
300
301 setAcceptDrops(true);
302 setAttribute(Qt::WA_NoMousePropagation);
303
304 // Random seed
[181]305 QTime now = QTime::currentTime();
306 qsrand(now.msec());
[112]307
[181]308 //loadSettings();
[112]309
310 // Save config every 5 minutes.
311 save_timer = new QTimer(this);
312 connect( save_timer, SIGNAL(timeout()), this, SLOT(maybeSaveSettings()) );
[181]313 save_timer->start( 5 * 60000 );
314
315#ifdef PLAYLIST_DOWNLOAD
316 downloader = new LoadPage(new QNetworkAccessManager(this), this);
317 downloader->setUserAgent("SMPlayer");
318 connect(downloader, SIGNAL(pageLoaded(QByteArray)), this, SLOT(playlistDownloaded(QByteArray)));
319 connect(downloader, SIGNAL(errorOcurred(int, QString)), this, SLOT(errorOcurred(int, QString)));
320
321 history_urls = new URLHistory;
[188]322 history_urls->addUrl("http://smplayer.info/sample.m3u8");
[181]323#endif
[112]324}
325
326Playlist::~Playlist() {
327 saveSettings();
[181]328 if (set) delete set;
329
330#ifdef PLAYLIST_DOWNLOAD
331 delete history_urls;
332#endif
[112]333}
334
[181]335void Playlist::setConfigPath(const QString & config_path) {
336 qDebug() << "Playlist::setConfigPath:" << config_path;
337
338 if (set) {
339 delete set;
340 set = 0;
341 }
342
343 if (!config_path.isEmpty()) {
344 QString inifile = config_path + "/playlist.ini";
345 qDebug() << "Playlist::setConfigPath: ini file:" << inifile;
346 set = new QSettings(inifile, QSettings::IniFormat);
347 loadSettings();
348 }
349}
350
[188]351void Playlist::updateWindowTitle() {
352 QString title;
[181]353
[188]354 title = playlist_filename;
355 if (title.isEmpty()) title = tr("Untitled playlist");
356 if (modified) title += " (*)";
357
358 qDebug() << "Playlist::updateWindowTitle:" << title;
359
360 setWindowTitle(title);
361 emit windowTitleChanged(title);
362}
363
364void Playlist::setPlaylistFilename(const QString & f) {
365 playlist_filename = f;
366 updateWindowTitle();
367}
368
[112]369void Playlist::setModified(bool mod) {
370 qDebug("Playlist::setModified: %d", mod);
371
372 modified = mod;
373 emit modifiedChanged(modified);
[188]374 updateWindowTitle();
[112]375}
376
377void Playlist::createTable() {
[181]378 table = new QStandardItemModel(this);
379 table->setColumnCount(COL_FILENAME + 1);
380 //table->setSortRole(Qt::UserRole + 1);
381
382 proxy = new QSortFilterProxyModel(this);
383 proxy->setSourceModel(table);
384 proxy->setSortRole(Qt::UserRole + 1);
385 proxy->setFilterRole(Qt::UserRole + 1);
386 proxy->setFilterKeyColumn(-1); // All columns
387
388#if USE_ITEM_DELEGATE
389 PlaylistDelegate * pl_delegate = new PlaylistDelegate(this);
390#endif
391
392 listView = new QTableView(this);
393 listView->setModel(proxy);
394
395#if USE_ITEM_DELEGATE
396 listView->setItemDelegateForColumn(COL_NAME, pl_delegate);
397 //listView->setItemDelegateForColumn(COL_FILENAME, pl_delegate);
398#endif
399
[112]400 listView->setObjectName("playlist_table");
401 listView->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
402 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
403 listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
404 listView->setContextMenuPolicy( Qt::CustomContextMenu );
405 listView->setShowGrid(false);
[181]406 listView->setSortingEnabled(true);
[188]407 listView->setWordWrap(false);
[181]408#if !USE_ITEM_DELEGATE
409 listView->setAlternatingRowColors(true);
410#endif
411 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
[165]412
[181]413 listView->verticalHeader()->hide();
414
[165]415#if QT_VERSION >= 0x050000
[181]416 MyScroller::setScroller(listView->viewport());
417
[165]418 listView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
[181]419// listView->horizontalHeader()->setSectionResizeMode(COL_NAME, QHeaderView::Stretch);
[165]420#else
[112]421 listView->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
[181]422// listView->horizontalHeader()->setResizeMode(COL_NAME, QHeaderView::Stretch);
423// listView->horizontalHeader()->setResizeMode(COL_FILENAME, QHeaderView::Interactive);
[165]424#endif
[181]425 listView->horizontalHeader()->setStretchLastSection(true);
[188]426 listView->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
[181]427
[112]428 /*
429 listView->horizontalHeader()->setResizeMode(COL_TIME, QHeaderView::ResizeToContents);
430 listView->horizontalHeader()->setResizeMode(COL_PLAY, QHeaderView::ResizeToContents);
431 */
[181]432 //listView->setIconSize( Images::icon("ok").size() );
[112]433
434#if DRAG_ITEMS
435 listView->setSelectionMode(QAbstractItemView::SingleSelection);
436 listView->setDragEnabled(true);
437 listView->setAcceptDrops(true);
438 listView->setDropIndicatorShown(true);
439 listView->setDragDropMode(QAbstractItemView::InternalMove);
440#endif
441
[181]442 connect(listView, SIGNAL(activated(const QModelIndex &)),
443 this, SLOT(itemActivated(const QModelIndex &)) );
[188]444
445 setFilenameColumnVisible(false);
[112]446}
447
448void Playlist::createActions() {
449 openAct = new MyAction(this, "pl_open", false);
450 connect( openAct, SIGNAL(triggered()), this, SLOT(load()) );
451
[181]452#ifdef PLAYLIST_DOWNLOAD
453 openUrlAct = new MyAction(this, "pl_open_url", false);
454 connect( openUrlAct, SIGNAL(triggered()), this, SLOT(openUrl()) );
455#endif
456
[112]457 saveAct = new MyAction(this, "pl_save", false);
[188]458 connect( saveAct, SIGNAL(triggered()), this, SLOT(saveCurrentPlaylist()) );
[112]459
[188]460 saveAsAct = new MyAction(this, "pl_save_as", false);
461 connect( saveAsAct, SIGNAL(triggered()), this, SLOT(save()) );
462
[112]463 playAct = new MyAction(this, "pl_play", false);
464 connect( playAct, SIGNAL(triggered()), this, SLOT(playCurrent()) );
465
466 nextAct = new MyAction(Qt::Key_N /*Qt::Key_Greater*/, this, "pl_next", false);
467 connect( nextAct, SIGNAL(triggered()), this, SLOT(playNext()) );
468
469 prevAct = new MyAction(Qt::Key_P /*Qt::Key_Less*/, this, "pl_prev", false);
470 connect( prevAct, SIGNAL(triggered()), this, SLOT(playPrev()) );
471
472 moveUpAct = new MyAction(this, "pl_move_up", false);
473 connect( moveUpAct, SIGNAL(triggered()), this, SLOT(upItem()) );
474
475 moveDownAct = new MyAction(this, "pl_move_down", false);
476 connect( moveDownAct, SIGNAL(triggered()), this, SLOT(downItem()) );
477
478 repeatAct = new MyAction(this, "pl_repeat", false);
479 repeatAct->setCheckable(true);
480
481 shuffleAct = new MyAction(this, "pl_shuffle", false);
482 shuffleAct->setCheckable(true);
483
484 // Add actions
485 addCurrentAct = new MyAction(this, "pl_add_current", false);
486 connect( addCurrentAct, SIGNAL(triggered()), this, SLOT(addCurrentFile()) );
487
488 addFilesAct = new MyAction(this, "pl_add_files", false);
489 connect( addFilesAct, SIGNAL(triggered()), this, SLOT(addFiles()) );
490
491 addDirectoryAct = new MyAction(this, "pl_add_directory", false);
492 connect( addDirectoryAct, SIGNAL(triggered()), this, SLOT(addDirectory()) );
493
[142]494 addUrlsAct = new MyAction(this, "pl_add_urls", false);
495 connect( addUrlsAct, SIGNAL(triggered()), this, SLOT(addUrls()) );
496
[112]497 // Remove actions
498 removeSelectedAct = new MyAction(this, "pl_remove_selected", false);
499 connect( removeSelectedAct, SIGNAL(triggered()), this, SLOT(removeSelected()) );
500
501 removeAllAct = new MyAction(this, "pl_remove_all", false);
502 connect( removeAllAct, SIGNAL(triggered()), this, SLOT(removeAll()) );
503
504 // Edit
505 editAct = new MyAction(this, "pl_edit", false);
506 connect( editAct, SIGNAL(triggered()), this, SLOT(editCurrentItem()) );
[176]507
[188]508#ifdef PLAYLIST_DELETE_FROM_DISK
[176]509 deleteSelectedFileFromDiskAct = new MyAction(this, "pl_delete_from_disk");
510 connect( deleteSelectedFileFromDiskAct, SIGNAL(triggered()), this, SLOT(deleteSelectedFileFromDisk()));
[188]511#endif
512
513 copyURLAct = new MyAction(this, "pl_copy_url");
514 connect( copyURLAct, SIGNAL(triggered()), this, SLOT(copyURL()));
515
516 openFolderAct = new MyAction(this, "pl_open_folder");
517 connect( openFolderAct, SIGNAL(triggered()), this, SLOT(openFolder()));
518
519#ifdef CHROMECAST_SUPPORT
520 playOnChromecastAct = new MyAction(this, "pl_chromecast");
521 connect( playOnChromecastAct, SIGNAL(triggered()), this, SLOT(playOnChromecast()));
522#else
523 openURLInWebAct = new MyAction(this, "pl_url_in_web");
524 connect( openURLInWebAct, SIGNAL(triggered()), this, SLOT(openURLInWeb()));
525#endif
526
527 showSearchAct = new MyAction(this, "pl_show_search", false);
528 showSearchAct->setCheckable(true);
529 connect(showSearchAct, SIGNAL(toggled(bool)), filter_edit, SLOT(setVisible(bool)));
530
531 showPositionColumnAct = new MyAction(this, "pl_show_position_column");
532 showPositionColumnAct->setCheckable(true);
533 connect(showPositionColumnAct, SIGNAL(toggled(bool)), this, SLOT(setPositionColumnVisible(bool)));
534
535 showNameColumnAct = new MyAction(this, "pl_show_name_column");
536 showNameColumnAct->setCheckable(true);
537 connect(showNameColumnAct, SIGNAL(toggled(bool)), this, SLOT(setNameColumnVisible(bool)));
538
539 showDurationColumnAct = new MyAction(this, "pl_show_duration_column");
540 showDurationColumnAct->setCheckable(true);
541 connect(showDurationColumnAct, SIGNAL(toggled(bool)), this, SLOT(setDurationColumnVisible(bool)));
542
543 showFilenameColumnAct = new MyAction(this, "pl_show_filename_column");
544 showFilenameColumnAct->setCheckable(true);
545 connect(showFilenameColumnAct, SIGNAL(toggled(bool)), this, SLOT(setFilenameColumnVisible(bool)));
[112]546}
547
548void Playlist::createToolbar() {
549 toolbar = new QToolBar(this);
550 toolbar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
[181]551 //toolbar->setIconSize(QSize(48,48));
[112]552
[181]553#ifdef PLAYLIST_DOUBLE_TOOLBAR
554 toolbar2 = new QToolBar(this);
555 toolbar2->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
556#endif
557
[188]558/*
[112]559 toolbar->addAction(openAct);
[181]560#ifdef PLAYLIST_DOWNLOAD
561 toolbar->addAction(openUrlAct);
562#endif
[112]563 toolbar->addAction(saveAct);;
564 toolbar->addSeparator();
[188]565*/
[112]566
[188]567 file_menu = new QMenu(this);
568 file_menu->addAction(openAct);
569 file_menu->addAction(saveAct);
570 file_menu->addAction(saveAsAct);
571#ifdef PLAYLIST_DOWNLOAD
572 file_menu->addAction(openUrlAct);
573#endif
574
575 file_button = new QToolButton(this);
576 file_button->setMenu(file_menu);
577 file_button->setPopupMode(QToolButton::InstantPopup);
578
[112]579 add_menu = new QMenu( this );
580 add_menu->addAction(addCurrentAct);
581 add_menu->addAction(addFilesAct );
582 add_menu->addAction(addDirectoryAct);
[142]583 add_menu->addAction(addUrlsAct);
[112]584
585 add_button = new QToolButton( this );
586 add_button->setMenu( add_menu );
587 add_button->setPopupMode(QToolButton::InstantPopup);
588
589 remove_menu = new QMenu( this );
590 remove_menu->addAction(removeSelectedAct);
591 remove_menu->addAction(removeAllAct);
592
593 remove_button = new QToolButton( this );
594 remove_button->setMenu( remove_menu );
595 remove_button->setPopupMode(QToolButton::InstantPopup);
596
[181]597
598#ifdef PLAYLIST_DOWNLOAD
599 QLabel * loading_label = new QLabel(this);
600 animation = new QMovie();
601 animation->setFileName(Images::file("pl_loading.gif"));
602 loading_label->setMovie(animation);
603#endif
604
[188]605 toolbar->addWidget(file_button);
606 toolbar->addSeparator();
[112]607 toolbar->addWidget(add_button);
608 toolbar->addWidget(remove_button);
609
610 toolbar->addSeparator();
611 toolbar->addAction(playAct);
612 toolbar->addAction(prevAct);
613 toolbar->addAction(nextAct);
[181]614#ifdef PLAYLIST_DOUBLE_TOOLBAR
615 toolbar2->addAction(moveUpAct);
616 toolbar2->addAction(moveDownAct);
617 toolbar2->addAction(repeatAct);
618 toolbar2->addAction(shuffleAct);
619 toolbar2->addSeparator();
620 toolbar2->addWidget(filter_edit);
621 #ifdef PLAYLIST_DOWNLOAD
622 loading_label_action = toolbar2->addWidget(loading_label);
623 #endif
624#else
[112]625 toolbar->addSeparator();
626 toolbar->addAction(repeatAct);
627 toolbar->addAction(shuffleAct);
628 toolbar->addSeparator();
629 toolbar->addAction(moveUpAct);
630 toolbar->addAction(moveDownAct);
[181]631 toolbar->addSeparator();
[188]632 toolbar->addAction(showSearchAct);
633 // toolbar->addWidget(filter_edit);
[181]634 #ifdef PLAYLIST_DOWNLOAD
635 loading_label_action = toolbar->addWidget(loading_label);
636 #endif
637#endif
[112]638
[181]639#ifdef PLAYLIST_DOWNLOAD
640 loading_label_action->setVisible(false);
641#endif
642
[112]643 // Popup menu
644 popup = new QMenu(this);
645 popup->addAction(playAct);
646 popup->addAction(removeSelectedAct);
647 popup->addAction(editAct);
[188]648#ifdef PLAYLIST_DELETE_FROM_DISK
[176]649 popup->addAction(deleteSelectedFileFromDiskAct);
[188]650#endif
651 popup->addAction(copyURLAct);
652 popup->addAction(openFolderAct);
653#ifdef CHROMECAST_SUPPORT
654 popup->addAction(playOnChromecastAct);
655#else
656 popup->addAction(openURLInWebAct);
657#endif
658 popup->addSeparator();
659 popup->addAction(showPositionColumnAct);
660 popup->addAction(showNameColumnAct);
661 popup->addAction(showDurationColumnAct);
662 popup->addAction(showFilenameColumnAct);
[112]663
664 connect( listView, SIGNAL(customContextMenuRequested(const QPoint &)),
665 this, SLOT(showPopup(const QPoint &)) );
666}
667
668void Playlist::retranslateStrings() {
[181]669 table->setHorizontalHeaderLabels(QStringList() << " " << tr("Name") << tr("Length") << tr("Filename / URL") );
[112]670
[188]671 openAct->change( Images::icon("open"), tr("&Load...") );
[181]672#ifdef PLAYLIST_DOWNLOAD
[188]673 openUrlAct->change( Images::icon("url"), tr("Load playlist from &URL...") );
[181]674 openUrlAct->setToolTip(tr("Download playlist from URL"));
675#endif
[112]676 saveAct->change( Images::icon("save"), tr("&Save") );
[188]677 saveAsAct->change( Images::icon("save"), tr("Save &as...") );
[112]678
679 playAct->change( tr("&Play") );
680
681 nextAct->change( tr("&Next") );
682 prevAct->change( tr("Pre&vious") );
683
[156]684 playAct->setIcon( Images::icon("play") );
685 nextAct->setIcon( Images::icon("next") );
686 prevAct->setIcon( Images::icon("previous") );
[112]687
688 moveUpAct->change( Images::icon("up"), tr("Move &up") );
689 moveDownAct->change( Images::icon("down"), tr("Move &down") );
690
691 repeatAct->change( Images::icon("repeat"), tr("&Repeat") );
692 shuffleAct->change( Images::icon("shuffle"), tr("S&huffle") );
693
694 // Add actions
695 addCurrentAct->change( tr("Add &current file") );
696 addFilesAct->change( tr("Add &file(s)") );
697 addDirectoryAct->change( tr("Add &directory") );
[142]698 addUrlsAct->change( tr("Add &URL(s)") );
[112]699
700 // Remove actions
[188]701 removeSelectedAct->change( Images::icon("delete"), tr("Remove &selected") );
[112]702 removeAllAct->change( tr("Remove &all") );
703
[188]704#ifdef PLAYLIST_DELETE_FROM_DISK
[176]705 deleteSelectedFileFromDiskAct->change( tr("&Delete file from disk") );
[188]706#endif
[176]707
[188]708 copyURLAct->change( Images::icon("copy"), tr("&Copy file path to clipboard") );
709 openFolderAct->change( Images::icon("openfolder"), tr("&Open source folder") );
710
711#ifdef CHROMECAST_SUPPORT
712 playOnChromecastAct->change( Images::icon("chromecast"), tr("Play on Chromec&ast") );
713#else
714 openURLInWebAct->change( tr("Open stream in &a web browser") );
715#endif
716
717 showSearchAct->change(Images::icon("find"), tr("Search"));
718
719 showPositionColumnAct->change(tr("Show position column"));
720 showNameColumnAct->change(tr("Show name column"));
721 showDurationColumnAct->change(tr("Show length column"));
722 showFilenameColumnAct->change(tr("Show filename column"));
723
[112]724 // Edit
725 editAct->change( tr("&Edit") );
726
727 // Tool buttons
[188]728 file_button->setIcon(Images::icon("open")); // FIXME: change icon
729 file_button->setToolTip(tr("Load/Save"));
[112]730 add_button->setIcon( Images::icon("plus") );
731 add_button->setToolTip( tr("Add...") );
732 remove_button->setIcon( Images::icon("minus") );
733 remove_button->setToolTip( tr("Remove...") );
734
[181]735 // Filter edit
736#if QT_VERSION >= 0x040700
737 filter_edit->setPlaceholderText(tr("Search"));
738#endif
739
[112]740 // Icon
741 setWindowIcon( Images::icon("logo", 64) );
[188]742 //setWindowTitle( tr( "SMPlayer - Playlist" ) );
[112]743}
744
745void Playlist::list() {
746 qDebug("Playlist::list");
747
[181]748 for (int n = 0; n < count(); n++) {
749 PLItem * i = itemData(n);
750 qDebug() << "Playlist::list: filename:" << i->filename() << "name:" << i->name() << "duration:" << i->duration();
[112]751 }
752}
753
[181]754void Playlist::setFilter(const QString & filter) {
755 proxy->setFilterWildcard(filter);
756}
[119]757
[181]758void Playlist::filterEditChanged(const QString & text) {
759 qDebug() << "Playlist::filterEditChanged:" << text;
760 setFilter(text);
[188]761
762 if (text.isEmpty()) {
763 qApp->processEvents();
764 listView->scrollTo(listView->currentIndex(), QAbstractItemView::PositionAtCenter);
765 }
[119]766}
767
[181]768void Playlist::setCurrentItem(int current) {
769 QModelIndex index = proxy->index(current, 0);
770 QModelIndex s_index = proxy->mapToSource(index);
[112]771
[181]772 //qDebug() << "Playlist::setCurrentItem: index:" << index.row() << "s_index:" << s_index.row();
[112]773
[181]774 int s_current = s_index.row();
[112]775
[181]776 PLItem * item = 0;
777 for (int n = 0; n < count(); n++) {
778 item = itemData(n);
779 if (n == s_current) {
780 item->setPlayed(true);
[112]781 }
[181]782 item->setCurrent( (n == s_current) );
[112]783 }
784
[181]785 listView->clearSelection();
786 listView->selectionModel()->setCurrentIndex(listView->model()->index(current, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
[112]787}
788
[181]789int Playlist::findCurrentItem() {
790 //qDebug("Playlist::findCurrentItem");
[112]791
[181]792 static int last_current = -1;
[112]793
[181]794 // Check if the last found current is still the current item to save time
795 PLItem * i = itemFromProxy(last_current);
796 if (i && i->isCurrent()) {
797 //qDebug() << "Playlist::findCurrentItem: return last_current:" << last_current;
798 return last_current;
[112]799 }
800
[181]801 for (int n = 0; n < proxy->rowCount(); n++) {
802 if (itemFromProxy(n)->isCurrent()) {
803 last_current = n;
804 return n;
805 }
[112]806 }
807
[181]808 return -1;
[112]809}
810
811void Playlist::clear() {
[181]812 table->setRowCount(0);
813 setCurrentItem(0);
814 setModified(false);
815}
[112]816
[181]817int Playlist::count() {
818 return table->rowCount();
819}
[112]820
[181]821bool Playlist::isEmpty() {
[188]822 return (table->rowCount() == 0);
[181]823}
[112]824
[181]825bool Playlist::existsItem(int row) {
826 return (row > -1 && row < table->rowCount());
[112]827}
828
[181]829PLItem * Playlist::itemData(int row) {
830 QStandardItem * i = table->item(row, COL_NAME);
831 return static_cast<PLItem*>(i);
[119]832}
833
[181]834PLItem * Playlist::itemFromProxy(int row) {
835 QModelIndex index = proxy->index(row, 0);
836 QModelIndex s_index = proxy->mapToSource(index);
837 //qDebug() << "Playlist::itemFromProxy: index is valid:" << index.isValid() << "s_index is valid:" << s_index.isValid();
838 if (index.isValid() && s_index.isValid()) {
839 return itemData(s_index.row());
840 } else {
841 return 0;
842 }
[112]843}
844
[181]845/*
846void Playlist::changeItem(int row, const QString & filename, const QString name, double duration, bool played, int pos) {
847 PLItem * i = itemData(row);
848
849 int position = row + 1;
850 if (pos != -1) position = pos;
851 i->setPosition(position);
852
853 i->setFilename(filename);
854 i->setName(name);
855 i->setDuration(duration);
856 i->setPlayed(played);
[112]857}
[181]858*/
[112]859
[188]860void Playlist::addItem(QString filename, QString name, double duration, QStringList params, QString video_url) {
[181]861 //qDebug() << "Playlist::addItem:" << filename;
[112]862
863 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
864 filename = Helper::changeSlashes(filename);
865 #endif
866
[181]867 if (name.isEmpty()) {
868 QFileInfo fi(filename);
869 // Let's see if it looks like a file (no dvd://1 or something)
870 if (filename.indexOf(QRegExp("^.*://.*")) == -1) {
871 // Local file
872 name = fi.fileName();
873 } else {
874 // Stream
875 name = filename;
876 }
877 }
878
879 PLItem * i = new PLItem(filename, name, duration);
[188]880 i->setExtraParams(params);
881 i->setVideoURL(video_url);
[181]882 i->setPosition(count()+1);
883 table->appendRow(i->items());
884
885 if (findCurrentItem() == -1) setCurrentItem(0);
886
887/*
[176]888#if !PL_ALLOW_DUPLICATES
[112]889 // Test if already is in the list
890 bool exists = false;
891 for ( int n = 0; n < pl.count(); n++) {
892 if ( pl[n].filename() == filename ) {
893 exists = true;
894 int last_item = pl.count()-1;
895 pl.move(n, last_item);
896 qDebug("Playlist::addItem: item already in list (%d), moved to %d", n, last_item);
897 if (current_item > -1) {
898 if (current_item > n) current_item--;
899 else
900 if (current_item == n) current_item = last_item;
901 }
902 break;
903 }
904 }
905
906 if (!exists) {
[176]907#endif
[112]908 if (name.isEmpty()) {
909 QFileInfo fi(filename);
910 // Let's see if it looks like a file (no dvd://1 or something)
911 if (filename.indexOf(QRegExp("^.*://.*")) == -1) {
912 // Local file
[165]913 name = fi.fileName(); //fi.baseName(true);
[112]914 } else {
915 // Stream
916 name = filename;
917 }
918 }
919 pl.append( PlaylistItem(filename, name, duration) );
920 //setModified( true ); // Better set the modified on a higher level
[176]921#if !PL_ALLOW_DUPLICATES
[112]922 } else {
923 qDebug("Playlist::addItem: item not added, already in the list");
924 }
[176]925#endif
[181]926*/
[112]927}
928
[119]929
[181]930void Playlist::load_m3u(QString file, M3UFormat format) {
931 bool utf8 = false;
932 if (format == DetectFormat) {
933 utf8 = (QFileInfo(file).suffix().toLower() == "m3u8");
934 } else {
935 utf8 = (format == M3U8);
936 }
[119]937
[181]938 qDebug() << "Playlist::load_m3u: utf8:" << utf8;
[119]939
[112]940 QRegExp m3u_id("^#EXTM3U|^#M3U");
941 QRegExp info("^#EXTINF:(.*),(.*)");
942
[176]943 QFile f( file );
944 if ( f.open( QIODevice::ReadOnly ) ) {
[112]945 playlist_path = QFileInfo(file).path();
946
947 clear();
948 QString filename="";
949 QString name="";
950 double duration=0;
[188]951 QStringList extra_params;
[112]952
[176]953 QTextStream stream( &f );
[112]954
955 if (utf8)
956 stream.setCodec("UTF-8");
957 else
958 stream.setCodec(QTextCodec::codecForLocale());
959
[176]960 QString line;
961 while ( !stream.atEnd() ) {
962 line = stream.readLine().trimmed();
963 if (line.isEmpty()) continue; // Ignore empty lines
964
965 qDebug( "Playlist::load_m3u: line: '%s'", line.toUtf8().data() );
[112]966 if (m3u_id.indexIn(line)!=-1) {
967 //#EXTM3U
968 // Ignore line
969 }
970 else
[181]971 /*
[112]972 if (info.indexIn(line)!=-1) {
973 duration = info.cap(1).toDouble();
974 name = info.cap(2);
[176]975 qDebug("Playlist::load_m3u: name: '%s', duration: %f", name.toUtf8().data(), duration );
[112]976 }
[181]977 */
978 if (line.startsWith("#EXTINF:")) {
979 QStringList fields = line.mid(8).split(",");
980 //qDebug() << "Playlist::load_m3u: fields:" << fields;
981 if (fields.count() >= 1) duration = fields[0].toDouble();
982 if (fields.count() >= 2) name = fields[1];
983 }
[112]984 else
[188]985 if (line.startsWith("#EXTVLCOPT:")) {
986 QString par = line.mid(11);
987 qDebug() << "Playlist::load_m3u: EXTVLCOPT:" << par;
988 extra_params << par;
989 }
990 else
[112]991 if (line.startsWith("#")) {
992 // Comment
993 // Ignore
994 } else {
995 filename = line;
996 QFileInfo fi(filename);
997 if (fi.exists()) {
998 filename = fi.absoluteFilePath();
999 }
1000 if (!fi.exists()) {
1001 if (QFileInfo( playlist_path + "/" + filename).exists() ) {
1002 filename = playlist_path + "/" + filename;
1003 }
1004 }
[188]1005 name.replace("&#44;", ",");
1006 //qDebug() << "Playlist::load_m3u: extra_params:" << extra_params;
1007 addItem( filename, name, duration, extra_params );
[112]1008 name="";
1009 duration = 0;
[188]1010 extra_params.clear();
[112]1011 }
[176]1012 }
1013 f.close();
[181]1014 //list();
[112]1015
[188]1016 setPlaylistFilename(file);
[112]1017 setModified( false );
1018
[188]1019 if (start_play_on_load) startPlay();
[112]1020 }
1021}
1022
1023void Playlist::load_pls(QString file) {
1024 qDebug("Playlist::load_pls");
1025
1026 if (!QFile::exists(file)) {
1027 qDebug("Playlist::load_pls: '%s' doesn't exist, doing nothing", file.toUtf8().constData());
1028 return;
1029 }
1030
1031 playlist_path = QFileInfo(file).path();
1032
1033 QSettings set(file, QSettings::IniFormat);
1034 set.beginGroup("playlist");
1035
1036 if (set.status() == QSettings::NoError) {
1037 clear();
1038 QString filename;
1039 QString name;
1040 double duration;
1041
1042 int num_items = set.value("NumberOfEntries", 0).toInt();
1043
1044 for (int n=0; n < num_items; n++) {
1045 filename = set.value("File"+QString::number(n+1), "").toString();
1046 name = set.value("Title"+QString::number(n+1), "").toString();
1047 duration = (double) set.value("Length"+QString::number(n+1), 0).toInt();
1048
1049 QFileInfo fi(filename);
1050 if (fi.exists()) {
1051 filename = fi.absoluteFilePath();
1052 }
1053 if (!fi.exists()) {
1054 if (QFileInfo( playlist_path + "/" + filename).exists() ) {
1055 filename = playlist_path + "/" + filename;
1056 }
1057 }
1058 addItem( filename, name, duration );
1059 }
1060 }
1061
1062 set.endGroup();
1063
[181]1064 //list();
[112]1065
[188]1066 setPlaylistFilename(file);
[112]1067 setModified( false );
1068
[188]1069 if (set.status() == QSettings::NoError && start_play_on_load) startPlay();
[112]1070}
1071
[176]1072void Playlist::loadXSPF(const QString & filename) {
1073 qDebug() << "Playlist::loadXSPF:" << filename;
1074
1075 QFile f(filename);
1076 if (!f.open(QIODevice::ReadOnly)) {
1077 return;
1078 }
1079
1080 QDomDocument dom_document;
1081 bool ok = dom_document.setContent(f.readAll());
1082 qDebug() << "Playlist::loadXSPF: success:" << ok;
1083 if (!ok) return;
1084
1085 QDomNode root = dom_document.documentElement();
1086 qDebug() << "Playlist::loadXSPF: tagname:" << root.toElement().tagName();
1087
1088 QDomNode child = root.firstChildElement("trackList");
1089 if (!child.isNull()) {
1090 clear();
1091
1092 qDebug() << "Playlist::loadXSPF: child:" << child.nodeName();
1093 QDomNode track = child.firstChildElement("track");
1094 while (!track.isNull()) {
1095 QString location = QUrl::fromPercentEncoding(track.firstChildElement("location").text().toLatin1());
1096 QString title = track.firstChildElement("title").text();
1097 int duration = track.firstChildElement("duration").text().toInt();
1098
1099 qDebug() << "Playlist::loadXSPF: location:" << location;
1100 qDebug() << "Playlist::loadXSPF: title:" << title;
1101 qDebug() << "Playlist::loadXSPF: duration:" << duration;
1102
1103 addItem( location, title, (double) duration / 1000 );
1104
1105 track = track.nextSiblingElement("track");
1106 }
1107
[181]1108 //list();
[188]1109 setPlaylistFilename(filename);
[176]1110 setModified( false );
[188]1111 if (start_play_on_load) startPlay();
[176]1112 }
1113}
1114
[112]1115bool Playlist::save_m3u(QString file) {
[181]1116 qDebug() << "Playlist::save_m3u:" << file;
[112]1117
1118 QString dir_path = QFileInfo(file).path();
1119 if (!dir_path.endsWith("/")) dir_path += "/";
1120
1121 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1122 dir_path = Helper::changeSlashes(dir_path);
1123 #endif
1124
[181]1125 qDebug() << "Playlist::save_m3u: dir_path:" << dir_path;
[112]1126
1127 bool utf8 = (QFileInfo(file).suffix().toLower() == "m3u8");
1128
1129 QFile f( file );
[181]1130 if ( f.open( QIODevice::WriteOnly ) ) {
1131 QTextStream stream( &f );
[112]1132
[181]1133 if (utf8)
[112]1134 stream.setCodec("UTF-8");
1135 else
1136 stream.setCodec(QTextCodec::codecForLocale());
1137
1138 QString filename;
[188]1139 QString name;
[112]1140
1141 stream << "#EXTM3U" << "\n";
[156]1142 stream << "# Playlist created by SMPlayer " << Version::printable() << " \n";
[112]1143
[181]1144 for (int n = 0; n < count(); n++) {
1145 PLItem * i = itemData(n);
1146 filename = i->filename();
[112]1147 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1148 filename = Helper::changeSlashes(filename);
1149 #endif
[188]1150 name = i->name();
1151 name.replace(",", "&#44;");
[112]1152 stream << "#EXTINF:";
[181]1153 stream << i->duration() << ",";
[188]1154 stream << name << "\n";
1155
1156 // Save extra params
1157 QStringList params = i->extraParams();
1158 foreach(QString par, params) {
1159 stream << "#EXTVLCOPT:" << par << "\n";
1160 }
1161
[112]1162 // Try to save the filename as relative instead of absolute
1163 if (filename.startsWith( dir_path )) {
1164 filename = filename.mid( dir_path.length() );
1165 }
1166 stream << filename << "\n";
1167 }
[181]1168 f.close();
[112]1169
[188]1170 setPlaylistFilename(file);
[112]1171 setModified( false );
1172 return true;
[181]1173 } else {
[112]1174 return false;
1175 }
1176}
1177
1178
1179bool Playlist::save_pls(QString file) {
[181]1180 qDebug() << "Playlist::save_pls:" << file;
[112]1181
1182 QString dir_path = QFileInfo(file).path();
1183 if (!dir_path.endsWith("/")) dir_path += "/";
1184
1185 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1186 dir_path = Helper::changeSlashes(dir_path);
1187 #endif
1188
[181]1189 qDebug() << "Playlist::save_pls: dir_path:" << dir_path;
[112]1190
1191 QSettings set(file, QSettings::IniFormat);
1192 set.beginGroup( "playlist");
[181]1193
[112]1194 QString filename;
1195
[181]1196 for (int n = 0; n < count(); n++) {
1197 PLItem * i = itemData(n);
1198 filename = i->filename();
[112]1199 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1200 filename = Helper::changeSlashes(filename);
1201 #endif
1202
1203 // Try to save the filename as relative instead of absolute
1204 if (filename.startsWith( dir_path )) {
1205 filename = filename.mid( dir_path.length() );
1206 }
1207
1208 set.setValue("File"+QString::number(n+1), filename);
[181]1209 set.setValue("Title"+QString::number(n+1), i->name());
1210 set.setValue("Length"+QString::number(n+1), (int) i->duration());
[112]1211 }
1212
[181]1213 set.setValue("NumberOfEntries", count());
[112]1214 set.setValue("Version", 2);
1215
1216 set.endGroup();
1217
1218 set.sync();
1219
1220 bool ok = (set.status() == QSettings::NoError);
[188]1221 if (ok) {
1222 setPlaylistFilename(file);
1223 setModified( false );
1224 }
[112]1225
1226 return ok;
1227}
1228
[176]1229bool Playlist::saveXSPF(const QString & filename) {
1230 qDebug() << "Playlist::saveXSPF:" << filename;
[112]1231
[176]1232 QFile f(filename);
1233 if (f.open( QIODevice::WriteOnly)) {
1234 QTextStream stream(&f);
1235 stream.setCodec("UTF-8");
1236
1237 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1238 stream << "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">\n";
1239 stream << "\t<trackList>\n";
1240
[181]1241 for (int n = 0; n < count(); n++) {
1242 PLItem * i = itemData(n);
1243 QString location = i->filename();
[176]1244 qDebug() << "Playlist::saveXSPF:" << location;
[181]1245
[176]1246 bool is_local = QFile::exists(location);
1247
1248 #ifdef Q_OS_WIN
1249 if (is_local) {
1250 location.replace("\\", "/");
1251 }
1252 #endif
1253 //qDebug() << "Playlist::saveXSPF:" << location;
1254
1255 QUrl url(location);
1256 location = url.toEncoded();
1257 //qDebug() << "Playlist::saveXSPF:" << location;
1258
1259 if (!location.startsWith("file:") && is_local) {
1260 #ifdef Q_OS_WIN
1261 location = "file:///" + location;
1262 #else
1263 location = "file://" + location;
1264 #endif
1265 }
1266
[181]1267 QString title = i->name();
1268 int duration = i->duration() * 1000;
[176]1269
1270 #if QT_VERSION >= 0x050000
1271 location = location.toHtmlEscaped();
1272 title = title.toHtmlEscaped();
1273 #else
1274 location = Qt::escape(location);
1275 title = Qt::escape(title);
1276 #endif
1277
1278 stream << "\t\t<track>\n";
1279 stream << "\t\t\t<location>" << location << "</location>\n";
1280 stream << "\t\t\t<title>" << title << "</title>\n";
1281 stream << "\t\t\t<duration>" << duration << "</duration>\n";
1282 stream << "\t\t</track>\n";
1283 }
1284
1285 stream << "\t</trackList>\n";
1286 stream << "</playlist>\n";
1287
[188]1288 setPlaylistFilename(filename);
[176]1289 setModified(false);
1290 return true;
1291 } else {
1292 return false;
1293 }
1294}
1295
1296
[112]1297void Playlist::load() {
1298 if (maybeSave()) {
1299 Extensions e;
1300 QString s = MyFileDialog::getOpenFileName(
1301 this, tr("Choose a file"),
1302 lastDir(),
[176]1303 tr("Playlists") + e.playlist().forFilter() + ";;" + tr("All files") +" (*)");
[112]1304
1305 if (!s.isEmpty()) {
1306 latest_dir = QFileInfo(s).absolutePath();
1307
[176]1308 QString suffix = QFileInfo(s).suffix().toLower();
1309 if (suffix == "pls") {
[112]1310 load_pls(s);
[176]1311 }
[112]1312 else
[176]1313 if (suffix == "xspf") {
1314 loadXSPF(s);
1315 }
1316 else {
[112]1317 load_m3u(s);
[176]1318 }
[181]1319 //listView->resizeColumnsToContents();
[112]1320 }
1321 }
1322}
1323
[188]1324bool Playlist::saveCurrentPlaylist() {
1325 qDebug("Playlist::saveCurrentPlaylist");
1326 return save(playlistFilename());
1327}
1328
1329bool Playlist::save(const QString & filename) {
1330 qDebug() << "Playlist::save:" << filename;
1331
1332 QString s = filename;
1333
1334 if (s.isEmpty()) {
1335 Extensions e;
1336 s = MyFileDialog::getSaveFileName(
[112]1337 this, tr("Choose a filename"),
1338 lastDir(),
[176]1339 tr("Playlists") + e.playlist().forFilter() + ";;" + tr("All files") +" (*)");
[188]1340 }
[112]1341
1342 if (!s.isEmpty()) {
1343 // If filename has no extension, add it
1344 if (QFileInfo(s).suffix().isEmpty()) {
1345 s = s + ".m3u";
1346 }
1347 if (QFileInfo(s).exists()) {
1348 int res = QMessageBox::question( this,
1349 tr("Confirm overwrite?"),
1350 tr("The file %1 already exists.\n"
1351 "Do you want to overwrite?").arg(s),
1352 QMessageBox::Yes,
1353 QMessageBox::No,
1354 QMessageBox::NoButton);
1355 if (res == QMessageBox::No ) {
1356 return false;
1357 }
1358 }
1359 latest_dir = QFileInfo(s).absolutePath();
1360
[176]1361 QString suffix = QFileInfo(s).suffix().toLower();
1362 if (suffix == "pls") {
[112]1363 return save_pls(s);
[176]1364 }
[112]1365 else
[176]1366 if (suffix == "xspf") {
1367 return saveXSPF(s);
1368 }
1369 else {
[112]1370 return save_m3u(s);
[176]1371 }
[112]1372
1373 } else {
1374 return false;
1375 }
1376}
1377
1378bool Playlist::maybeSave() {
1379 if (!isModified()) return true;
1380
1381 int res = QMessageBox::question( this,
1382 tr("Playlist modified"),
1383 tr("There are unsaved changes, do you want to save the playlist?"),
1384 QMessageBox::Yes,
1385 QMessageBox::No,
1386 QMessageBox::Cancel);
1387
1388 switch (res) {
1389 case QMessageBox::No : return true; // Discard changes
1390 case QMessageBox::Cancel : return false; // Cancel operation
1391 default : return save();
1392 }
1393}
1394
1395void Playlist::playCurrent() {
[181]1396 int current = listView->currentIndex().row();
[112]1397 if (current > -1) {
1398 playItem(current);
1399 }
1400}
1401
[181]1402void Playlist::itemActivated(const QModelIndex & index ) {
1403 qDebug() << "Playlist::itemActivated: row:" << index.row();
1404 playItem(index.row());
[112]1405}
1406
1407void Playlist::showPopup(const QPoint & pos) {
1408 qDebug("Playlist::showPopup: x: %d y: %d", pos.x(), pos.y() );
1409
[188]1410 QModelIndex index = listView->currentIndex();
1411 if (!index.isValid()) {
1412 playAct->setEnabled(false);
1413 removeSelectedAct->setEnabled(false);
1414 editAct->setEnabled(false);
1415 #ifdef PLAYLIST_DELETE_FROM_DISK
1416 deleteSelectedFileFromDiskAct->setEnabled(false);
1417 #endif
1418 copyURLAct->setEnabled(false);
1419 openFolderAct->setEnabled(false);
1420 #ifdef CHROMECAST_SUPPORT
1421 playOnChromecastAct->setEnabled(false);
1422 #else
1423 openURLInWebAct->setEnabled(false);
1424 #endif
1425 } else {
1426 playAct->setEnabled(true);
1427 removeSelectedAct->setEnabled(true);
1428 editAct->setEnabled(true);
1429 #ifdef PLAYLIST_DELETE_FROM_DISK
1430 deleteSelectedFileFromDiskAct->setEnabled(true);
1431 #endif
1432 copyURLAct->setEnabled(true);
1433 openFolderAct->setEnabled(true);
1434 #ifdef CHROMECAST_SUPPORT
1435 playOnChromecastAct->setEnabled(true);
1436 #else
1437 openURLInWebAct->setEnabled(true);
1438 #endif
1439
1440 QModelIndex s_index = proxy->mapToSource(index);
1441 int current = s_index.row();
1442 PLItem * i = itemData(current);
1443 QString filename = i->filename();
1444 QFileInfo fi(filename);
1445
1446 if (fi.exists()) {
1447 copyURLAct->setText( tr("&Copy file path to clipboard") );
1448 #ifndef CHROMECAST_SUPPORT
1449 openURLInWebAct->setEnabled(false);
1450 #endif
1451 } else {
1452 copyURLAct->setText( tr("&Copy URL to clipboard") );
1453 openFolderAct->setEnabled(false);
1454 }
1455 }
1456
[112]1457 if (!popup->isVisible()) {
1458 popup->move( listView->viewport()->mapToGlobal(pos) );
1459 popup->show();
1460 }
1461}
1462
1463void Playlist::startPlay() {
1464 // Start to play
1465 if ( shuffleAct->isChecked() )
1466 playItem( chooseRandomItem() );
1467 else
1468 playItem(0);
1469}
1470
1471void Playlist::playItem( int n ) {
[181]1472 qDebug("Playlist::playItem: %d (count: %d)", n, proxy->rowCount());
[112]1473
[181]1474 if ( (n >= proxy->rowCount()) || (n < 0) ) {
[112]1475 qDebug("Playlist::playItem: out of range");
1476 emit playlistEnded();
1477 return;
1478 }
1479
[188]1480 PLItem * i = itemFromProxy(n);
1481 QString filename = i->filename();
1482 QStringList params = i->extraParams();
1483
[112]1484 if (!filename.isEmpty()) {
1485 setCurrentItem(n);
[188]1486
1487 if (!params.isEmpty()) {
1488 emit requestToPlayStream(filename, params);
[181]1489 } else {
[188]1490 if (play_files_from_start) {
1491 emit requestToPlayFile(filename, 0);
1492 } else {
1493 emit requestToPlayFile(filename);
1494 }
[181]1495 }
[112]1496 }
1497}
1498
1499void Playlist::playNext() {
1500 qDebug("Playlist::playNext");
1501
1502 if (shuffleAct->isChecked()) {
1503 // Shuffle
1504 int chosen_item = chooseRandomItem();
[176]1505 qDebug("Playlist::playNext: chosen_item: %d", chosen_item);
[112]1506 if (chosen_item == -1) {
1507 clearPlayedTag();
[176]1508 if (repeatAct->isChecked()) {
1509 chosen_item = chooseRandomItem();
1510 if (chosen_item == -1) chosen_item = 0;
1511 }
[112]1512 }
[181]1513 playItem(chosen_item);
[112]1514 } else {
[181]1515 int current = findCurrentItem();
1516 bool finished_list = (current + 1 >= proxy->rowCount());
[112]1517 if (finished_list) clearPlayedTag();
1518
[181]1519 if (repeatAct->isChecked() && finished_list) {
[112]1520 playItem(0);
1521 } else {
[181]1522 playItem(current + 1);
[112]1523 }
1524 }
1525}
1526
1527void Playlist::playPrev() {
1528 qDebug("Playlist::playPrev");
[181]1529 int current = findCurrentItem() - 1;
1530 if (current >= 0) {
1531 playItem(current);
[112]1532 } else {
[181]1533 if (proxy->rowCount() > 1) playItem(proxy->rowCount() - 1);
[112]1534 }
1535}
1536
[188]1537void Playlist::playNextAuto() {
1538 qDebug("Playlist::playNextAuto");
1539 if (automatically_play_next) {
1540 playNext();
1541 } else {
1542 emit playlistEnded();
1543 }
1544}
[165]1545
1546void Playlist::resumePlay() {
[181]1547 qDebug("Playlist::resumePlay");
1548
1549 if (count() > 0) {
1550 int current = findCurrentItem();
1551 if (current < 0) current = 0;
1552 playItem(current);
[165]1553 }
1554}
1555
[181]1556void Playlist::getMediaInfo(const MediaData & mdat) {
[176]1557 qDebug("Playlist::getMediaInfo");
[112]1558
[181]1559 QString filename = mdat.filename;
1560 double duration = mdat.duration;
1561 QString artist = mdat.clip_artist;
[188]1562 QString video_url = mdat.stream_path;
[112]1563
1564 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
1565 filename = Helper::changeSlashes(filename);
1566 #endif
1567
[181]1568 QString name;
1569 if (change_name) {
1570 name = mdat.clip_name;
1571 if (name.isEmpty()) name = mdat.stream_title;
1572
1573 if (name.isEmpty()) {
1574 QFileInfo fi(filename);
1575 if (fi.exists()) {
1576 // Local file
1577 name = fi.fileName();
1578 } else {
1579 // Stream
1580 name = filename;
1581 }
[112]1582 }
[181]1583 if (!artist.isEmpty()) name = artist + " - " + name;
[112]1584 }
1585
[181]1586 for (int n = 0; n < count(); n++) {
1587 PLItem * i = itemData(n);
1588 if (i->filename() == filename) {
[176]1589 // Found item
[181]1590 bool modified_name = !(i->filename().endsWith(i->name()));
1591 if (i->duration() < 1) {
1592 if (!modified_name && !name.isEmpty()) {
1593 i->setName(name);
[112]1594 }
[181]1595 i->setDuration(duration);
[176]1596 }
1597 else
[112]1598 // Edited name (sets duration to 1)
[181]1599 if (i->duration() == 1) {
1600 i->setDuration(duration);
[112]1601 }
[188]1602 i->setVideoURL(video_url);
[112]1603 }
1604 }
1605}
1606
1607// Add current file to playlist
1608void Playlist::addCurrentFile() {
1609 qDebug("Playlist::addCurrentFile");
[181]1610 emit requestToAddCurrentFile();
[112]1611}
1612
1613void Playlist::addFiles() {
[119]1614 Extensions e;
[112]1615 QStringList files = MyFileDialog::getOpenFileNames(
1616 this, tr("Select one or more files to open"),
1617 lastDir(),
[119]1618 tr("Multimedia") + e.multimedia().forFilter() + ";;" +
[112]1619 tr("All files") +" (*.*)" );
1620
[188]1621 if (files.count() != 0) {
1622 addFiles(files);
1623 setModified(true);
1624 }
[112]1625}
1626
1627void Playlist::addFiles(QStringList files, AutoGetInfo auto_get_info) {
1628 qDebug("Playlist::addFiles");
1629
[181]1630 #if USE_INFOPROVIDER
[112]1631 bool get_info = (auto_get_info == GetInfo);
1632 if (auto_get_info == UserDefined) {
1633 get_info = automatically_get_info;
1634 }
1635
1636 MediaData data;
1637 setCursor(Qt::WaitCursor);
[181]1638 #endif
[112]1639
[176]1640 QString initial_file;
[181]1641 if (count() == 1) initial_file = itemData(0)->filename();
[176]1642 int new_current_item = -1;
1643
1644 for (int n = 0; n < files.count(); n++) {
1645 QString name = "";
1646 double duration = 0;
[181]1647 #if USE_INFOPROVIDER
[176]1648 if ( (get_info) && (QFile::exists(files[n])) ) {
1649 data = InfoProvider::getInfo(files[n]);
1650 name = data.displayName();
1651 duration = data.duration;
[112]1652 //qApp->processEvents();
1653 }
[181]1654 #endif
[112]1655
[176]1656 //qDebug() << "Playlist::addFiles: comparing:" << initial_file << "with" << files[n];
1657
1658 if (!initial_file.isEmpty() && files[n] == initial_file) {
[181]1659 PLItem * first_item = itemData(0);
1660 name = first_item->name();
1661 duration = first_item->duration();
1662 table->removeRow(0);
[176]1663 new_current_item = n;
[112]1664 }
[176]1665 addItem(files[n], name, duration);
[112]1666
[176]1667 if (QFile::exists(files[n])) {
1668 latest_dir = QFileInfo(files[n]).absolutePath();
1669 }
1670 }
[181]1671 #if USE_INFOPROVIDER
[112]1672 unsetCursor();
[181]1673 #endif
[176]1674
1675 if (new_current_item != -1) setCurrentItem(new_current_item);
[112]1676
[181]1677 qDebug() << "Playlist::addFiles: latest_dir:" << latest_dir;
[112]1678}
1679
1680void Playlist::addFile(QString file, AutoGetInfo auto_get_info) {
1681 addFiles( QStringList() << file, auto_get_info );
1682}
1683
1684void Playlist::addDirectory() {
1685 QString s = MyFileDialog::getExistingDirectory(
1686 this, tr("Choose a directory"),
1687 lastDir() );
1688
1689 if (!s.isEmpty()) {
1690 addDirectory(s);
1691 latest_dir = s;
1692 }
1693}
1694
[142]1695void Playlist::addUrls() {
1696 MultilineInputDialog d(this);
1697 if (d.exec() == QDialog::Accepted) {
1698 QStringList urls = d.lines();
1699 foreach(QString u, urls) {
1700 if (!u.isEmpty()) addItem( u, "", 0 );
1701 }
[188]1702 setModified(true);
[142]1703 }
1704}
1705
[112]1706void Playlist::addOneDirectory(QString dir) {
1707 QStringList filelist;
1708
1709 Extensions e;
1710 QRegExp rx_ext(e.multimedia().forRegExp());
1711 rx_ext.setCaseSensitivity(Qt::CaseInsensitive);
1712
1713 QStringList dir_list = QDir(dir).entryList();
1714
1715 QString filename;
[181]1716 QStringList::Iterator it = dir_list.begin();
1717 while( it != dir_list.end() ) {
[112]1718 filename = dir;
1719 if (filename.right(1)!="/") filename += "/";
1720 filename += (*it);
1721 QFileInfo fi(filename);
1722 if (!fi.isDir()) {
1723 if (rx_ext.indexIn(fi.suffix()) > -1) {
1724 filelist << filename;
1725 }
1726 }
1727 ++it;
1728 }
1729 addFiles(filelist);
1730}
1731
1732void Playlist::addDirectory(QString dir) {
1733 addOneDirectory(dir);
1734
1735 if (recursive_add_directory) {
1736 QFileInfoList dir_list = QDir(dir).entryInfoList(QStringList() << "*", QDir::AllDirs | QDir::NoDotAndDotDot);
1737 for (int n=0; n < dir_list.count(); n++) {
1738 if (dir_list[n].isDir()) {
1739 qDebug("Playlist::addDirectory: adding directory: %s", dir_list[n].filePath().toUtf8().data());
1740 addDirectory(dir_list[n].filePath());
1741 }
1742 }
1743 }
[188]1744 setModified(true);
[112]1745}
1746
1747// Remove selected items
1748void Playlist::removeSelected() {
1749 qDebug("Playlist::removeSelected");
1750
[181]1751 QModelIndexList indexes = listView->selectionModel()->selectedRows();
1752 int count = indexes.count();
[112]1753
[181]1754 for (int n = count; n > 0; n--) {
1755 QModelIndex s_index = proxy->mapToSource(indexes.at(n-1));
1756 table->removeRow(s_index.row());
1757 setModified(true);
[112]1758 }
1759
1760 if (isEmpty()) setModified(false);
1761
[181]1762 if (findCurrentItem() == -1) {
1763 int current = indexes.at(0).row() - 1;
1764 if (current < 0) current = 0;
1765 setCurrentItem(current);
[112]1766 }
1767}
1768
1769void Playlist::removeAll() {
1770 clear();
[188]1771 setPlaylistFilename("");
[112]1772}
1773
1774void Playlist::clearPlayedTag() {
[181]1775 for (int n = 0; n < count(); n++) {
1776 itemData(n)->setPlayed(false);
[112]1777 }
1778}
1779
1780int Playlist::chooseRandomItem() {
1781 qDebug( "Playlist::chooseRandomItem");
[176]1782
[181]1783 QList<int> fi; //List of not played items (free items)
1784 for (int n = 0; n < proxy->rowCount(); n++) {
1785 if (!itemFromProxy(n)->played()) fi.append(n);
[112]1786 }
1787
[176]1788 qDebug("Playlist::chooseRandomItem: free items: %d", fi.count() );
[112]1789
[176]1790 if (fi.count() == 0) return -1; // none free
[112]1791
[176]1792 qDebug("Playlist::chooseRandomItem: items: ");
1793 for (int i = 0; i < fi.count(); i++) {
1794 qDebug("Playlist::chooseRandomItem: * item: %d", fi[i]);
[112]1795 }
1796
[181]1797 int selected = (qrand() % fi.count());
[176]1798 qDebug("Playlist::chooseRandomItem: selected item: %d (%d)", selected, fi[selected]);
[112]1799 return fi[selected];
1800}
1801
[181]1802void Playlist::upItem() {
1803 QModelIndex index = listView->currentIndex();
1804 QModelIndex s_index = proxy->mapToSource(index);
[112]1805
[181]1806 QModelIndex prev = listView->model()->index(index.row()-1, 0);
1807 QModelIndex s_prev = proxy->mapToSource(prev);
[112]1808
[181]1809 qDebug() << "Playlist::upItem: row:" << index.row() << "source row:" << s_index.row();
1810 qDebug() << "Playlist::upItem: previous row:" << prev.row() << "previous source row:" << s_prev.row();
[112]1811
[181]1812 if (s_index.isValid() && s_prev.isValid()) {
1813 int row = s_index.row();
1814 int prev_row = s_prev.row();
[112]1815
[181]1816 int pos_num_current = itemData(row)->position();
1817 int pos_num_prev = itemData(prev_row)->position();
[119]1818
[181]1819 qDebug() << "Playlist::upItem: pos_num_current:" << pos_num_current << "pos_num_prev:" << pos_num_prev;
1820
1821 itemData(row)->setPosition(pos_num_prev);
1822 itemData(prev_row)->setPosition(pos_num_current);
1823
1824 QList<QStandardItem*> cells = table->takeRow(row);
1825 table->insertRow(s_prev.row(), cells);
1826 listView->selectionModel()->setCurrentIndex(listView->model()->index(index.row()-1, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
[188]1827
1828 setModified(true);
[181]1829 }
[119]1830}
1831
1832void Playlist::downItem() {
1833 qDebug("Playlist::downItem");
1834
[181]1835 QModelIndex index = listView->currentIndex();
1836 QModelIndex s_index = proxy->mapToSource(index);
[119]1837
[181]1838 QModelIndex next = listView->model()->index(index.row()+1, 0);
1839 QModelIndex s_next = proxy->mapToSource(next);
[119]1840
[181]1841 qDebug() << "Playlist::downItem: row:" << index.row() << "source row:" << s_index.row();
1842 qDebug() << "Playlist::downItem: next row:" << next.row() << "next source row:" << s_next.row();
[119]1843
[181]1844 if (s_index.isValid() && s_next.isValid()) {
1845 int row = s_index.row();
1846 int next_row = s_next.row();
[112]1847
[181]1848 int pos_num_current = itemData(row)->position();
1849 int pos_num_next = itemData(next_row)->position();
1850
1851 qDebug() << "Playlist::downItem: pos_num_current:" << pos_num_current << "pos_num_next:" << pos_num_next;
1852
1853 itemData(row)->setPosition(pos_num_next);
1854 itemData(next_row)->setPosition(pos_num_current);
1855
1856 QList<QStandardItem*> cells = table->takeRow(row);
1857 table->insertRow(s_next.row(), cells);
1858 listView->selectionModel()->setCurrentIndex(listView->model()->index(index.row()+1, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
[188]1859
1860 setModified(true);
[112]1861 }
1862}
1863
1864void Playlist::editCurrentItem() {
[181]1865 QModelIndex v_index = listView->currentIndex();
1866 QModelIndex s_index = proxy->mapToSource(v_index);
1867 qDebug() << "Playlist::editCurrentItem: row:" << v_index.row() << "source row:" << s_index.row();
1868 int current = s_index.row();
[112]1869 if (current > -1) editItem(current);
1870}
1871
[181]1872void Playlist::editItem(int row) {
1873 qDebug() << "Playlist::editItem:" << row;
[112]1874
[181]1875 PLItem * i = itemData(row);
1876 QString current_name = i->name();
1877 if (current_name.isEmpty()) current_name = i->filename();
1878
[112]1879 bool ok;
1880 QString text = QInputDialog::getText( this,
[181]1881 tr("Edit name"),
1882 tr("Type the name that will be displayed in the playlist for this file:"),
[112]1883 QLineEdit::Normal,
1884 current_name, &ok );
[188]1885 if ( ok && !text.isEmpty() ) {
[181]1886 // user entered something and pressed OK
1887 i->setName(text);
[112]1888
1889 // If duration == 0 the name will be overwritten!
[181]1890 if (i->duration() < 1) i->setDuration(1);
[112]1891
1892 setModified( true );
[181]1893 }
[112]1894}
1895
[188]1896#ifdef PLAYLIST_DELETE_FROM_DISK
[176]1897void Playlist::deleteSelectedFileFromDisk() {
1898 qDebug("Playlist::deleteSelectedFileFromDisk");
1899
[181]1900 QModelIndex index = listView->currentIndex();
1901 if (!index.isValid()) return;
[176]1902
[181]1903 QModelIndex s_index = proxy->mapToSource(index);
[176]1904
[181]1905 qDebug() << "Playlist::deleteSelectedFileFromDisk: row:" << index.row() << "source row:" << s_index.row();
1906 int current = s_index.row();
[176]1907
[181]1908 // Select only the current row
1909 listView->selectionModel()->setCurrentIndex(listView->model()->index(index.row(), 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
[176]1910
[181]1911 QString filename = itemData(current)->filename();
1912 qDebug() << "Playlist::deleteSelectedFileFromDisk: current file:" << filename;
1913
1914 QFileInfo fi(filename);
1915 if (fi.exists() && fi.isFile() && fi.isWritable()) {
1916 // Ask the user for confirmation
1917 int res = QMessageBox::question(this, tr("Confirm deletion"),
1918 tr("You're about to DELETE the file '%1' from your drive.").arg(filename) + "<br>"+
1919 tr("This action cannot be undone. Are you sure you want to proceed?"),
1920 QMessageBox::Yes, QMessageBox::No);
1921
1922 if (res == QMessageBox::Yes) {
1923 // Delete file
1924 #if SIMULATE_FILE_DELETION
1925 bool success = true;
1926 #else
1927 bool success = QFile::remove(filename);
1928 #endif
1929
1930 if (success) {
1931 // Remove item from the playlist
1932 table->removeRow(current);
1933 if (findCurrentItem() == -1) {
1934 if (current > 0) setCurrentItem(current-1); else setCurrentItem(0);
[176]1935 }
[181]1936 } else {
1937 QMessageBox::warning(this, tr("Deletion failed"),
1938 tr("It wasn't possible to delete '%1'").arg(filename));
[176]1939 }
1940 }
[181]1941 } else {
1942 qDebug("Playlist::deleteSelectedFileFromDisk: file doesn't exists, it's not a file or it's not writable");
1943 QMessageBox::information(this, tr("Error deleting the file"),
1944 tr("It's not possible to delete '%1' from the filesystem.").arg(filename));
[176]1945 }
1946}
[188]1947#endif
[176]1948
[188]1949void Playlist::copyURL() {
1950 qDebug("Playlist::copyURL");
1951
1952 QModelIndexList indexes = listView->selectionModel()->selectedRows();
1953 int count = indexes.count();
1954
1955 QString text;
1956
1957 for (int n = 0; n < count; n++) {
1958 QModelIndex s_index = proxy->mapToSource(indexes.at(n));
1959 int current = s_index.row();
1960 text += itemData(current)->filename();
1961 if (n < count-1) {
1962 #ifdef Q_OS_WIN
1963 text += "\r\n";
1964 #else
1965 text += "\n";
1966 #endif
1967 }
1968 }
1969
1970 if (!text.isEmpty()) QApplication::clipboard()->setText(text);
1971}
1972
1973void Playlist::openFolder() {
1974 qDebug("Playlist::openFolder");
1975
1976 QModelIndex index = listView->currentIndex();
1977 if (!index.isValid()) return;
1978 QModelIndex s_index = proxy->mapToSource(index);
1979 int current = s_index.row();
1980 PLItem * i = itemData(current);
1981 QString filename = i->filename();
1982
1983 qDebug() << "Playlist::openFolder: filename:" << filename;
1984
1985 QFileInfo fi(filename);
1986 if (fi.exists()) {
1987 QString src_folder = fi.absolutePath();
1988 QDesktopServices::openUrl(QUrl::fromLocalFile(src_folder));
1989 }
1990}
1991
1992#ifdef CHROMECAST_SUPPORT
1993void Playlist::playOnChromecast() {
1994 qDebug("Playlist::playOnChromecast");
1995
1996 QModelIndex index = listView->currentIndex();
1997 if (!index.isValid()) return;
1998 QModelIndex s_index = proxy->mapToSource(index);
1999 int current = s_index.row();
2000 PLItem * i = itemData(current);
2001 QString filename = i->filename();
2002 QString video_url = i->videoURL();
2003
2004 QString url = filename;
2005 if (!video_url.isEmpty()) url = video_url;
2006
2007 if (QFile::exists(filename)) {
2008 Chromecast::instance()->openLocal(url, i->name());
2009 } else {
2010 Chromecast::instance()->openStream(url, i->name());
2011 }
2012}
2013#else
2014void Playlist::openURLInWeb() {
2015 qDebug("Playlist::openURLInWeb");
2016
2017 QModelIndex index = listView->currentIndex();
2018 if (!index.isValid()) return;
2019 QModelIndex s_index = proxy->mapToSource(index);
2020 int current = s_index.row();
2021 PLItem * i = itemData(current);
2022 QString filename = i->filename();
2023 QString video_url = i->videoURL();
2024
2025 QString url = filename;
2026 if (!video_url.isEmpty()) url = video_url;
2027
2028 QDesktopServices::openUrl(QUrl(url));
2029}
2030#endif
2031
[112]2032// Drag&drop
2033void Playlist::dragEnterEvent( QDragEnterEvent *e ) {
2034 qDebug("Playlist::dragEnterEvent");
2035
2036 if (e->mimeData()->hasUrls()) {
2037 e->acceptProposedAction();
2038 }
2039}
2040
2041void Playlist::dropEvent( QDropEvent *e ) {
2042 qDebug("Playlist::dropEvent");
2043
2044 QStringList files;
2045
2046 if (e->mimeData()->hasUrls()) {
2047 QList <QUrl> l = e->mimeData()->urls();
2048 QString s;
2049 for (int n=0; n < l.count(); n++) {
2050 if (l[n].isValid()) {
2051 qDebug("Playlist::dropEvent: scheme: '%s'", l[n].scheme().toUtf8().data());
2052 if (l[n].scheme() == "file")
2053 s = l[n].toLocalFile();
2054 else
2055 s = l[n].toString();
2056 /*
2057 qDebug(" * '%s'", l[n].toString().toUtf8().data());
2058 qDebug(" * '%s'", l[n].toLocalFile().toUtf8().data());
2059 */
2060 qDebug("Playlist::dropEvent: file: '%s'", s.toUtf8().data());
2061 files.append(s);
2062 }
2063 }
2064 }
2065
[165]2066 #ifdef Q_OS_WIN
2067 files = Helper::resolveSymlinks(files); // Check for Windows shortcuts
2068 #endif
[170]2069 files.sort();
[119]2070
[112]2071 QStringList only_files;
2072 for (int n = 0; n < files.count(); n++) {
2073 if ( QFileInfo( files[n] ).isDir() ) {
2074 addDirectory( files[n] );
2075 } else {
2076 only_files.append( files[n] );
2077 }
2078 }
[188]2079
2080 if (only_files.count() == 1) {
2081 // Check if the file is a playlist
2082 QString filename = only_files[0];
2083 QFileInfo fi(filename);
2084 QString extension = fi.suffix().toLower();
2085 if (extension == "m3u8" || extension == "m3u") { load_m3u(filename); return; }
2086 else
2087 if (extension == "pls") { load_pls(filename); return; }
2088 else
2089 if (extension == "xspf") { loadXSPF(filename); return; }
2090 }
2091
[112]2092 addFiles( only_files );
[188]2093 setModified(true);
[112]2094}
2095
[119]2096
[112]2097void Playlist::hideEvent( QHideEvent * ) {
2098 emit visibilityChanged(false);
2099}
2100
2101void Playlist::showEvent( QShowEvent * ) {
2102 emit visibilityChanged(true);
2103}
2104
2105void Playlist::closeEvent( QCloseEvent * e ) {
2106 saveSettings();
2107 e->accept();
2108}
2109
[176]2110void Playlist::playerFailed(QProcess::ProcessError e) {
2111 qDebug("Playlist::playerFailed");
2112 if (ignore_player_errors) {
2113 if (e != QProcess::FailedToStart) {
2114 playNext();
2115 }
2116 }
2117}
[112]2118
[176]2119void Playlist::playerFinishedWithError(int e) {
2120 qDebug("Playlist::playerFinishedWithError: %d", e);
2121 if (ignore_player_errors) {
2122 playNext();
2123 }
2124}
2125
[112]2126void Playlist::maybeSaveSettings() {
2127 qDebug("Playlist::maybeSaveSettings");
2128 if (isModified()) saveSettings();
2129}
2130
2131void Playlist::saveSettings() {
2132 qDebug("Playlist::saveSettings");
2133
[181]2134 if (!set) return;
[112]2135
2136 set->beginGroup( "playlist");
2137
2138 set->setValue( "repeat", repeatAct->isChecked() );
2139 set->setValue( "shuffle", shuffleAct->isChecked() );
2140
2141 set->setValue( "auto_get_info", automatically_get_info );
2142 set->setValue( "recursive_add_directory", recursive_add_directory );
2143 set->setValue( "save_playlist_in_config", save_playlist_in_config );
2144 set->setValue( "play_files_from_start", play_files_from_start );
[188]2145 set->setValue( "start_play_on_load", start_play_on_load );
[112]2146 set->setValue( "automatically_play_next", automatically_play_next );
[176]2147 set->setValue( "ignore_player_errors", ignore_player_errors );
[181]2148 set->setValue( "change_name", change_name );
[112]2149
2150 set->setValue( "row_spacing", row_spacing );
2151
2152#if !DOCK_PLAYLIST
2153 set->setValue( "size", size() );
2154#endif
2155
[188]2156 set->setValue(QString("header_state/2/%1").arg(Helper::qtVersion()), listView->horizontalHeader()->saveState());
2157
[181]2158 set->setValue( "sort_column", proxy->sortColumn() );
2159 set->setValue( "sort_order", proxy->sortOrder() );
[188]2160 set->setValue( "filter_case_sensitive", filterCaseSensitive() );
[181]2161 set->setValue( "filter", filter_edit->text() );
[188]2162 set->setValue( "sort_case_sensitive", sortCaseSensitive() );
2163 set->setValue( "auto_sort", autoSort() );
[181]2164
[188]2165 set->setValue( "show_search", showSearchAct->isChecked() );
2166
[112]2167 set->endGroup();
2168
[181]2169 set->beginGroup( "directories");
2170 set->setValue("save_dirs", save_dirs);
2171 set->setValue("latest_dir", save_dirs ? latest_dir : "" );
2172 set->endGroup();
2173
[112]2174 if (save_playlist_in_config) {
2175 //Save current list
[181]2176 set->beginGroup("playlist_contents");
2177 set->beginWriteArray("items");
2178 //set->setValue( "count", count() );
2179 for (int n = 0; n < count(); n++ ) {
2180 set->setArrayIndex(n);
2181 PLItem * i = itemData(n);
2182 set->setValue( QString("item_%1_filename").arg(n), i->filename() );
2183 set->setValue( QString("item_%1_duration").arg(n), i->duration() );
2184 set->setValue( QString("item_%1_name").arg(n), i->name() );
[188]2185 set->setValue( QString("item_%1_params").arg(n), i->extraParams() );
2186 set->setValue( QString("item_%1_video_url").arg(n), i->videoURL() );
[112]2187 }
[181]2188 set->endArray();
2189 set->setValue( "current_item", findCurrentItem() );
[188]2190 set->setValue("filename", playlistFilename());
[112]2191 set->setValue( "modified", modified );
2192
2193 set->endGroup();
2194 }
[181]2195
2196#ifdef PLAYLIST_DOWNLOAD
2197 set->beginGroup("history");
2198 set->setValue("max_items", history_urls->maxItems());
2199 set->setValue("urls", history_urls->toStringList());
2200 set->endGroup();
2201#endif
2202
2203 if (set->contains("playlist/change_title")) set->remove("playlist/change_title");
[188]2204 if (set->contains("playlist/sort_case_sensivity")) set->remove("playlist/sort_case_sensivity");
2205 if (set->contains("playlist/filter_case_sensivity")) set->remove("playlist/filter_case_sensivity");
[112]2206}
2207
2208void Playlist::loadSettings() {
2209 qDebug("Playlist::loadSettings");
2210
[181]2211 if (!set) return;
[112]2212
2213 set->beginGroup( "playlist");
2214
2215 repeatAct->setChecked( set->value( "repeat", repeatAct->isChecked() ).toBool() );
2216 shuffleAct->setChecked( set->value( "shuffle", shuffleAct->isChecked() ).toBool() );
2217
2218 automatically_get_info = set->value( "auto_get_info", automatically_get_info ).toBool();
2219 recursive_add_directory = set->value( "recursive_add_directory", recursive_add_directory ).toBool();
2220 save_playlist_in_config = set->value( "save_playlist_in_config", save_playlist_in_config ).toBool();
2221 play_files_from_start = set->value( "play_files_from_start", play_files_from_start ).toBool();
[188]2222 start_play_on_load = set->value( "start_play_on_load", start_play_on_load ).toBool();
[112]2223 automatically_play_next = set->value( "automatically_play_next", automatically_play_next ).toBool();
[176]2224 ignore_player_errors = set->value( "ignore_player_errors", ignore_player_errors ).toBool();
[181]2225 change_name = set->value( "change_name", change_name ).toBool();
[112]2226
2227 row_spacing = set->value( "row_spacing", row_spacing ).toInt();
2228
2229#if !DOCK_PLAYLIST
2230 resize( set->value("size", size()).toSize() );
2231#endif
2232
[188]2233 listView->horizontalHeader()->restoreState(set->value(QString("header_state/2/%1").arg(Helper::qtVersion()), QByteArray()).toByteArray());
2234
[181]2235 int sort_column = set->value("sort_column", COL_NUM).toInt();
2236 int sort_order = set->value("sort_order", Qt::AscendingOrder).toInt();
[188]2237 bool filter_case_sensitive = set->value("filter_case_sensitive", false).toBool();
[181]2238 QString filter = set->value( "filter").toString();
[188]2239 bool sort_case_sensitive = set->value("sort_case_sensitive", false).toBool();
2240 bool auto_sort = set->value("auto_sort", false).toBool();
[112]2241
[188]2242 showSearchAct->setChecked( set->value( "show_search", false).toBool() );
2243
[112]2244 set->endGroup();
2245
[181]2246 set->beginGroup( "directories");
2247 save_dirs = set->value("save_dirs", save_dirs).toBool();
2248 if (save_dirs) {
2249 latest_dir = set->value("latest_dir", latest_dir).toString();
2250 }
2251 set->endGroup();
2252
[112]2253 if (save_playlist_in_config) {
2254 //Load latest list
[181]2255 set->beginGroup("playlist_contents");
2256 int count = set->beginReadArray("items");
[112]2257
2258 QString filename, name;
2259 double duration;
[181]2260 for (int n = 0; n < count; n++) {
2261 set->setArrayIndex(n);
[112]2262 filename = set->value( QString("item_%1_filename").arg(n), "" ).toString();
2263 duration = set->value( QString("item_%1_duration").arg(n), -1 ).toDouble();
2264 name = set->value( QString("item_%1_name").arg(n), "" ).toString();
[188]2265 QStringList params = set->value( QString("item_%1_params").arg(n), QStringList()).toStringList();
2266 QString video_url = set->value( QString("item_%1_video_url").arg(n), "").toString();
2267 addItem( filename, name, duration, params, video_url );
[112]2268 }
[181]2269 set->endArray();
[112]2270 setCurrentItem( set->value( "current_item", -1 ).toInt() );
[188]2271 setPlaylistFilename( set->value("filename", "").toString() );
[112]2272 setModified( set->value( "modified", false ).toBool() );
2273
2274 set->endGroup();
[181]2275 //listView->resizeColumnsToContents();
[112]2276 }
[181]2277
2278#ifdef PLAYLIST_DOWNLOAD
2279 set->beginGroup("history");
2280 history_urls->setMaxItems(set->value("max_items", 50).toInt());
2281 history_urls->fromStringList( set->value("urls", history_urls->toStringList()).toStringList() );
2282 set->endGroup();
2283#endif
2284
[188]2285 setFilterCaseSensitive(filter_case_sensitive);
2286 setSortCaseSensitive(sort_case_sensitive);
[181]2287 proxy->sort(sort_column, (Qt::SortOrder) sort_order);
2288 filter_edit->setText(filter);
[188]2289 setAutoSort(auto_sort);
2290
2291 if (!listView->isColumnHidden(COL_NUM)) showPositionColumnAct->setChecked(true);
2292 if (!listView->isColumnHidden(COL_NAME)) showNameColumnAct->setChecked(true);
2293 if (!listView->isColumnHidden(COL_TIME)) showDurationColumnAct->setChecked(true);
2294 if (!listView->isColumnHidden(COL_FILENAME)) showFilenameColumnAct->setChecked(true);
[112]2295}
2296
2297QString Playlist::lastDir() {
2298 QString last_dir = latest_dir;
2299 return last_dir;
2300}
2301
[188]2302void Playlist::setPositionColumnVisible(bool b) {
2303 listView->setColumnHidden(COL_NUM, !b);
2304}
2305
2306void Playlist::setNameColumnVisible(bool b) {
2307 listView->setColumnHidden(COL_NAME, !b);
2308}
2309
2310void Playlist::setDurationColumnVisible(bool b) {
2311 listView->setColumnHidden(COL_TIME, !b);
2312}
2313
2314void Playlist::setFilenameColumnVisible(bool b) {
2315 listView->setColumnHidden(COL_FILENAME, !b);
2316}
2317
2318void Playlist::setAutoSort(bool b) {
2319 proxy->setDynamicSortFilter(b);
2320}
2321
2322bool Playlist::autoSort() {
2323 return proxy->dynamicSortFilter();
2324}
2325
2326void Playlist::setSortCaseSensitive(bool b) {
2327 Qt::CaseSensitivity c = b ? Qt::CaseSensitive : Qt::CaseInsensitive;
2328 proxy->setSortCaseSensitivity(c);
2329}
2330
2331bool Playlist::sortCaseSensitive() {
2332 return (proxy->sortCaseSensitivity() == Qt::CaseSensitive);
2333}
2334
2335void Playlist::setFilterCaseSensitive(bool b) {
2336 Qt::CaseSensitivity c = b ? Qt::CaseSensitive : Qt::CaseInsensitive;
2337 proxy->setFilterCaseSensitivity(c);
2338}
2339
2340bool Playlist::filterCaseSensitive() {
2341 return (proxy->filterCaseSensitivity() == Qt::CaseSensitive);
2342}
2343
[181]2344#ifdef PLAYLIST_DOWNLOAD
2345void Playlist::openUrl() {
2346 qDebug("Playlist::openUrl");
2347
2348 InputURL d(this);
2349
2350 // Get url from clipboard
2351 QString clipboard_text = QApplication::clipboard()->text();
2352 if (!clipboard_text.isEmpty() && clipboard_text.contains("://")) {
2353 d.setURL(clipboard_text);
2354 }
2355
2356 for (int n = 0; n < history_urls->count(); n++) {
2357 d.setURL(history_urls->url(n));
2358 }
2359
2360 if (d.exec() == QDialog::Accepted ) {
2361 QString url = d.url();
2362 if (!url.isEmpty()) {
2363 history_urls->addUrl(url);
2364 openUrl(url);
2365 }
2366 }
2367}
2368
2369void Playlist::openUrl(const QString & url) {
2370 qDebug() << "Playlist::openUrl:" << url;
2371 downloader->fetchPage(url);
2372
2373 showLoadingAnimation(true);
2374}
2375
2376void Playlist::playlistDownloaded(QByteArray data) {
2377 qDebug("Playlist::playlistDownloaded");
2378 // Save to a temporary file
2379 QTemporaryFile tf;
2380 tf.open();
2381 tf.write(data);
2382 tf.close();
2383 QString tfile = tf.fileName();
2384 qDebug() << "Playlist::playlistDownloaded: tfile:" << tfile;
2385
2386 if (data.contains("#EXTM3U")) {
2387 load_m3u(tfile, M3U8);
[188]2388 setPlaylistFilename("");
[181]2389 }
2390 else
2391 if (data.contains("[playlist]")) {
2392 load_pls(tfile);
[188]2393 setPlaylistFilename("");
[181]2394 }
2395 else
2396 if (data.contains("xspf.org")) {
2397 loadXSPF(tfile);
[188]2398 setPlaylistFilename("");
[181]2399 }
2400 else {
2401 QMessageBox::warning(this, "SMPlayer", tr("It's not possible to load this playlist") +": "+ tr("Unrecognized format."));
2402 }
2403
2404 showLoadingAnimation(false);
2405}
2406
2407void Playlist::errorOcurred(int error_number, QString error_str) {
2408 showLoadingAnimation(false);
2409
2410 qDebug() << "Playlist::errorOcurred:" << error_number << ":" << error_str;
2411 QMessageBox::warning(this, "SMPlayer", error_str);
2412}
2413
2414void Playlist::showLoadingAnimation(bool b) {
2415 if (b) animation->start(); else animation->stop();
2416 loading_label_action->setVisible(b);
2417}
2418
2419void Playlist::setMaxItemsUrlHistory(int max_items) {
2420 history_urls->setMaxItems(max_items);
2421}
2422
2423int Playlist::maxItemsUrlHistory() {
2424 return history_urls->maxItems();
2425}
2426#endif
2427
[112]2428// Language change stuff
2429void Playlist::changeEvent(QEvent *e) {
2430 if (e->type() == QEvent::LanguageChange) {
2431 retranslateStrings();
2432 } else {
2433 QWidget::changeEvent(e);
2434 }
2435}
2436
2437#include "moc_playlist.cpp"
Note: See TracBrowser for help on using the repository browser.