├── README.md
├── YPT_Beta_UI.png
├── main.py
├── requirements.txt
├── ytpd_beta.py
└── ytpd_beta.ui
/README.md:
--------------------------------------------------------------------------------
1 | # YouTube Playlist Downloader
2 | Simple youtube playlist downloader.
3 | It's mostly meant to download music as it only downloads videos in a MP4 format.
4 |
5 | Beta version, there are still bugs. Loading bigger playlists may take a few minutes, around 3 minutes for a 200 song playlist.
6 |
7 | If something went wrong (program didn't work correctly) it could be due to an internal error, but is most likely due to:
8 |
9 | ### Possible Errors (Program not working correctly):
10 | #### Playlist didn't load up:
11 | - Playlist is set to private
12 | - Bad internet connection (it has to be really bad to not work)
13 | - Incorrect URL (it's not a YouTube playlist)
14 | - Unknown error
15 |
16 | #### If a certain video wasn't downloaded:
17 | - It was deleted from the playlist (no longer exists)
18 | - It was blocked by YouTube (could be any reason)
19 | - Blocked in country
20 | - Bad internet connection
21 | - Server connection error or unavailable format
22 | - Unknown error
23 |
24 |
25 | ## Guide
26 | 
27 |
28 | - Paste/type in the URL of your playlist, check that the playlist is set to public, and then press Load.
29 | - Loading videos from playlist may take a while, depending on the size of the playlist. Around 5 seconds for a 10 song playlist.
30 | - You can only load one playlist and download the one.
31 | - Once the videos are loaded (they pop up in the box to the right), type in the name of the new folder in which it will be downloaded.
32 | - Click apply to change to the folder name (auto set to \YTPD_Beta)
33 | - Click Download to download music files, a loading bar will pop up.
34 | - This downloads all the files to the selected folder on Desktop (can not be changed).
35 |
36 | Use at your own risk, as downloading certain music/videos might be illegal due to copyrights.
37 |
38 |
39 |
--------------------------------------------------------------------------------
/YPT_Beta_UI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/15minutOdmora/YouTubePlaylistDownloader/3eaf783b171ac5d55e96d903d9c8b5e281100c39/YPT_Beta_UI.png
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from pytube import YouTube, Playlist
3 | from PyQt5.QtCore import QThread, pyqtSignal
4 | from PyQt5.QtWidgets import *
5 | from ytpd_beta import UiMainWindow
6 | import os
7 |
8 |
9 | def seconds_to_mmss(seconds):
10 | """Function:
11 | Returns a string in the format of mm:ss"""
12 | min = seconds // 60
13 | sec = seconds % 60
14 | if min < 10:
15 | min_str = '0' + str(min)
16 | else:
17 | min_str = str(min)
18 | if sec < 10:
19 | sec_str = '0' + str(sec)
20 | else:
21 | sec_str = str(sec)
22 | return min_str + ":" + sec_str
23 |
24 |
25 | class DownloadingVideos(QThread):
26 | """ Download all videos from the videos_dict using the id, todo fix some bugs"""
27 | downloadCount = pyqtSignal(int, int, bool, str) # downloaded, number_of_videos, finished
28 |
29 | def __init__(self, videos_dict, download_path, parent=None):
30 | QThread.__init__(self, parent)
31 | self.videos_dict = videos_dict
32 | self.download_path = download_path
33 |
34 | def run(self):
35 | """ Main function, downloads videos by their id while emitting progress data"""
36 | # Create download folder before downloading
37 | if not os.path.isdir(self.download_path): # if path doesn't exist, create one.
38 | os.mkdir(self.download_path)
39 | # Download
40 | number_of_videos = len(self.videos_dict)
41 | failed_download = list()
42 | downloaded, now_downloading, finished = 0, "", False
43 | for key, value in self.videos_dict.items():
44 | try:
45 | stream = value["stream"]
46 | print(key, ": stream=", stream)
47 | _ = stream.download(output_path=self.download_path)
48 | except:
49 | failed_download.append(key)
50 | print("Unable to download: ", key)
51 | downloaded += 1
52 | now_downloading = key
53 | if downloaded == number_of_videos:
54 | finished = True
55 | self.downloadCount.emit(downloaded, number_of_videos, finished, now_downloading)
56 | if len(failed_download) == 0:
57 | print("Download was successful, all videos downloaded.")
58 | else:
59 | print("Download wasn't as successfull, these videos weren't downloaded: ")
60 | for title in failed_download:
61 | print(title)
62 |
63 |
64 | class UrlLoading(QThread):
65 | """ Loads the videos data from playlist in another thread."""
66 | countChanged = pyqtSignal(dict, bool)
67 |
68 | def __init__(self, playlist_link, parent=None):
69 | QThread.__init__(self, parent)
70 | self.playlist_link = playlist_link
71 |
72 | def run(self):
73 | """ Main function, gets all the playlist videos data, emits the info dict"""
74 | videos_dict = dict()
75 | try:
76 | print("Parsing playlist successful, loading video data...")
77 | playlist = Playlist(self.playlist_link)
78 | for video in playlist.videos:
79 | print(video.title, "... loaded")
80 | try:
81 | video.check_availability()
82 | except: # If video is unavailable skip, print error to console
83 | print("Unable to load: ", video.title)
84 | continue
85 | videos_dict[video.title] = dict()
86 | # Save stream object to directly download
87 | videos_dict[video.title]["stream"] = video.streams.filter(only_audio=True, file_extension='mp4').first()
88 | videos_dict[video.title]["duration"] = seconds_to_mmss(video.length)
89 | # Send data dict after each song is loaded
90 | self.countChanged.emit(videos_dict, True)
91 | except:
92 | print("Exception(Playlist/Video Error)... Something went wrong with loading playlist or one of the videos.")
93 | self.countChanged.emit({}, False)
94 |
95 |
96 | class MainPage(QMainWindow, UiMainWindow):
97 | def __init__(self, parent=None):
98 | super(MainPage, self).__init__(parent)
99 | self.setupUi(self)
100 | # Hide the fetching data label and the error label, shows up when its loading, invalid url
101 | self.url_fetching_data_label.hide()
102 | self.url_error_label.hide()
103 | # Seting the videos dict. and connecting the delete video button with the remove_selected_items fn.
104 | self.remove_from_list_button.clicked.connect(self.remove_selected_items)
105 | self.videos_dict = dict()
106 | # Hide progress bar, show it when it starts downloading
107 | self.progressBar.hide()
108 | # Buttons Connection with the appropriate functions
109 | self.url_load_button.clicked.connect(self.url_loading_button_click)
110 | self.download_button.clicked.connect(self.download_button_click)
111 | self.folder_name_button.clicked.connect(self.select_save_path)
112 | # Get the desktop path, set folder name, full download path, set label.
113 | self.current_path = os.path.dirname(os.path.abspath(__file__))
114 | self.download_folder_name = "YTPD_beta"
115 | self.download_full_path = self.current_path + "\\" + self.download_folder_name
116 | self.download_path_label.setText("Download path: {}".format(self.download_full_path))
117 |
118 |
119 | # Input url threading
120 |
121 | def url_loading_button_click(self):
122 | """ Reads input data from url_input and creates an instance of the UrlLoading thread """
123 | self.listWidget.clear() # Clear the widget
124 | self.videos_dict = dict() # Clear the dict
125 | self.url_fetching_data_label.show() # Show the loading label
126 | self.url_error_label.hide() # Hide the error label if the input is a retry
127 | playlist_url = self.url_input.text() # Get the input text
128 | self.calc = UrlLoading(playlist_url) # Pass in the input text
129 | self.calc.countChanged.connect(self.url_loading_finished) # connect with the changing variables
130 | self.calc.start()
131 |
132 | def url_loading_finished(self, videos_dict, executed):
133 | """ Retrieves data from thread at the end, updates the list"""
134 | self.url_fetching_data_label.hide() # Hide the loading label as it has finished loading
135 | if executed: # If it was executed successfully
136 | videos_list, counter = list(), 0
137 | for key, value in videos_dict.items():
138 | counter += 1
139 | line_str = str(counter) + ") " + key + " " + videos_dict[key]["duration"] # Display line
140 | videos_list.append(line_str)
141 | self.videos_dict = videos_dict
142 | self.listWidget.addItems(videos_list) # Update the list with the strings
143 | else:
144 | self.url_error_label.show() # Show the error label
145 |
146 | # Downloading videos threading
147 |
148 | def download_button_click(self):
149 | """ Executes when the button is clicked """
150 | self.progressBar.show()
151 | self.download_button.setEnabled(False)
152 | self.down = DownloadingVideos(self.videos_dict, self.download_full_path) # Pass in the dict
153 | self.down.downloadCount.connect(self.downloading_update) # connect with the download function
154 | self.down.start()
155 |
156 | def downloading_update(self, downloaded, number_of_videos, finished, now_downloading): # int, bool
157 | """ Executes as it receives signals from thread """
158 | # Update the progressBar, calculate the perc. here
159 | downloaded_percentages = int(round((downloaded / number_of_videos) * 100))
160 | self.progressBar.setProperty("value", downloaded_percentages)
161 | # Changing the downloaded label
162 | if finished:
163 | self.download_button.setEnabled(True)
164 | self.progressBar.hide()
165 | self.downloaded_label.setText("Finished. {} files saved to:\n {}".format(downloaded, self.download_full_path))
166 | self.progressBar.setProperty("value", 0) # reset loading bar
167 | else:
168 | self.downloaded_label.setText("{}\nDownloaded {} out of {}.".format(now_downloading, downloaded,
169 | number_of_videos))
170 |
171 | # Items videos from videos(of playlist) list
172 |
173 | def remove_selected_items(self):
174 | """ Removes the selected items from the self.videos_dict and self.list_of_titles
175 | Also refreshes the listWidget"""
176 | list_items = self.listWidget.selectedItems() # list of the selected items (should not be a list but okay)
177 | selected_item_index = self.listWidget.currentRow() # index of the selected item
178 | if not list_items: # if nothing is selected
179 | return
180 | for item in list_items: # iterate through the list and delete the items
181 | self.listWidget.takeItem(self.listWidget.row(item))
182 |
183 | if selected_item_index != -1: # -1 means nothing was selected
184 | index, delete_key = 0, ''
185 | for key, value in self.videos_dict.items():
186 | if index == selected_item_index:
187 | delete_key = key # save the key of the deleted item in the videos_dict
188 | index += 1
189 | del self.videos_dict[delete_key] # delete the actual key
190 |
191 | # Getting input from the folder name folder_name_input, creating dir, changing labels
192 |
193 | def select_save_path(self):
194 | """ Sets the download path to the input one, auto set to YTPD_beta"""
195 | file = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
196 | self.download_full_path = file
197 | self.download_path_label.setText("Download path: \n{}".format(self.download_full_path))
198 | """self.download_folder_name = self.folder_name_input.text()
199 | self.download_full_path = self.current_path + "\\" + self.download_folder_name
200 | self.download_path_label.setText("Download path: \n{}".format(self.download_full_path))"""
201 |
202 |
203 | if __name__ == "__main__":
204 | app = QApplication(sys.argv)
205 | widget = MainPage()
206 | widget.show()
207 | sys.exit(app.exec_())
208 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pytube3==9.6.4
2 | PyQt5==5.15.4
3 | pytube==10.7.2
4 |
--------------------------------------------------------------------------------
/ytpd_beta.py:
--------------------------------------------------------------------------------
1 | from PyQt5 import QtCore, QtGui, QtWidgets
2 |
3 |
4 | class UiMainWindow(object):
5 | """ GUI Design class """
6 | def setupUi(self, MainWindow):
7 | """ GUI style and display settings"""
8 | MainWindow.setObjectName("MainWindow")
9 | MainWindow.resize(827, 498)
10 | font = QtGui.QFont()
11 | font.setBold(True)
12 | font.setUnderline(False)
13 | font.setWeight(75)
14 | MainWindow.setFont(font)
15 | self.centralwidget = QtWidgets.QWidget(MainWindow)
16 | self.centralwidget.setObjectName("centralwidget")
17 | # Main title
18 | self.title_label = QtWidgets.QLabel(self.centralwidget)
19 | self.title_label.setGeometry(QtCore.QRect(10, 0, 271, 41))
20 | font = QtGui.QFont()
21 | font.setFamily("Segoe UI Emoji")
22 | font.setPointSize(14)
23 | font.setBold(True)
24 | font.setUnderline(False)
25 | font.setWeight(75)
26 | self.title_label.setFont(font)
27 | self.title_label.setObjectName("title_label")
28 | # Video title list display
29 | self.listWidget = QtWidgets.QListWidget(self.centralwidget)
30 | self.listWidget.setGeometry(QtCore.QRect(520, 40, 281, 301))
31 | self.listWidget.setObjectName("listWidget")
32 | # URL input label
33 | self.enter_playlist_url_label = QtWidgets.QLabel(self.centralwidget)
34 | self.enter_playlist_url_label.setGeometry(QtCore.QRect(10, 70, 131, 21))
35 | font = QtGui.QFont()
36 | font.setPointSize(10)
37 | font.setBold(True)
38 | font.setUnderline(False)
39 | font.setWeight(75)
40 | self.enter_playlist_url_label.setFont(font)
41 | self.enter_playlist_url_label.setObjectName("enter_playlist_url_label")
42 | # URL input box
43 | self.url_input = QtWidgets.QLineEdit(self.centralwidget)
44 | self.url_input.setGeometry(QtCore.QRect(140, 70, 301, 20))
45 | self.url_input.setObjectName("url_input")
46 | # URL load button
47 | self.url_load_button = QtWidgets.QPushButton(self.centralwidget)
48 | self.url_load_button.setGeometry(QtCore.QRect(370, 100, 71, 23))
49 | self.url_load_button.setObjectName("url_load_button")
50 | # Remove from QList buton
51 | self.remove_from_list_button = QtWidgets.QPushButton(self.centralwidget)
52 | self.remove_from_list_button.setGeometry(QtCore.QRect(670, 350, 131, 23))
53 | self.remove_from_list_button.setObjectName("remove_from_list_button")
54 | # Loading bar
55 | self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
56 | self.progressBar.setGeometry(QtCore.QRect(10, 390, 481, 16))
57 | self.progressBar.setProperty("value", 0)
58 | self.progressBar.setObjectName("progressBar")
59 | # Error label
60 | self.url_error_label = QtWidgets.QLabel(self.centralwidget)
61 | self.url_error_label.setGeometry(QtCore.QRect(140, 50, 350, 16))
62 | font = QtGui.QFont()
63 | font.setBold(False)
64 | font.setUnderline(False)
65 | font.setWeight(50)
66 | self.url_error_label.setFont(font)
67 | self.url_error_label.setObjectName("url_error_label")
68 | # Loading data label
69 | self.url_fetching_data_label = QtWidgets.QLabel(self.centralwidget)
70 | self.url_fetching_data_label.setGeometry(QtCore.QRect(140, 100, 231, 20))
71 | font = QtGui.QFont()
72 | font.setBold(False)
73 | font.setUnderline(False)
74 | font.setWeight(50)
75 | self.url_fetching_data_label.setFont(font)
76 | self.url_fetching_data_label.setObjectName("url_fetching_data_label")
77 | # Download folder label
78 | """self.download_folder_name_label = QtWidgets.QLabel(self.centralwidget)
79 | self.download_folder_name_label.setGeometry(QtCore.QRect(10, 280, 161, 21))
80 | font = QtGui.QFont()
81 | font.setPointSize(10)
82 | self.download_folder_name_label.setFont(font)
83 | self.download_folder_name_label.setObjectName("download_folder_name_label")"""
84 | # Folder name input box
85 | """self.folder_name_input = QtWidgets.QLineEdit(self.centralwidget)
86 | self.folder_name_input.setGeometry(QtCore.QRect(170, 280, 211, 20))
87 | self.folder_name_input.setObjectName("folder_name_input")"""
88 | # Folder name load button
89 | self.folder_name_button = QtWidgets.QPushButton(self.centralwidget)
90 | self.folder_name_button.setGeometry(QtCore.QRect(390, 280, 100, 23))
91 | self.folder_name_button.setObjectName("folder_name_button")
92 | # Download folder name input box
93 | self.download_path_label = QtWidgets.QLabel(self.centralwidget)
94 | self.download_path_label.setGeometry(QtCore.QRect(10, 295, 700, 30))
95 | font = QtGui.QFont()
96 | font.setPointSize(9)
97 | font.setBold(True)
98 | font.setUnderline(False)
99 | # font.setWeight(50)
100 | self.download_path_label.setFont(font)
101 | self.download_path_label.setObjectName("download_path_label")
102 | # Description label
103 | self.guide_label = QtWidgets.QLabel(self.centralwidget)
104 | self.guide_label.setGeometry(QtCore.QRect(10, 130, 451, 111))
105 | font = QtGui.QFont()
106 | font.setPointSize(8)
107 | font.setBold(False)
108 | font.setUnderline(False)
109 | font.setWeight(50)
110 | self.guide_label.setFont(font)
111 | self.guide_label.setObjectName("guide_label")
112 | # Lists widget label
113 | self.listWidget_label = QtWidgets.QLabel(self.centralwidget)
114 | self.listWidget_label.setGeometry(QtCore.QRect(520, 20, 201, 16))
115 | font = QtGui.QFont()
116 | font.setBold(False)
117 | font.setUnderline(False)
118 | font.setWeight(50)
119 | self.listWidget_label.setFont(font)
120 | self.listWidget_label.setObjectName("listWidget_label")
121 | # Download button
122 | self.download_button = QtWidgets.QPushButton(self.centralwidget)
123 | self.download_button.setGeometry(QtCore.QRect(10, 350, 91, 23))
124 | self.download_button.setObjectName("download_button")
125 | # Donload label
126 | self.downloaded_label = QtWidgets.QLabel(self.centralwidget)
127 | self.downloaded_label.setGeometry(QtCore.QRect(520, 390, 350, 30))
128 | font = QtGui.QFont()
129 | font.setBold(False)
130 | font.setUnderline(False)
131 | font.setWeight(50)
132 | self.downloaded_label.setFont(font)
133 | self.downloaded_label.setObjectName("downloaded_label")
134 | # Bottom credits label
135 | self.credits_label = QtWidgets.QLabel(self.centralwidget)
136 | self.credits_label.setGeometry(QtCore.QRect(10, 460, 581, 16))
137 | font = QtGui.QFont()
138 | font.setBold(False)
139 | font.setUnderline(False)
140 | font.setWeight(50)
141 | self.credits_label.setFont(font)
142 | self.credits_label.setObjectName("credits_label")
143 | # Bottom credits hyperlink
144 | self.link_label = QtWidgets.QLabel(self.centralwidget)
145 | self.link_label.setGeometry(QtCore.QRect(220, 460, 300, 16))
146 | font = QtGui.QFont()
147 | font.setBold(False)
148 | font.setUnderline(True)
149 | font.setWeight(50)
150 | self.link_label.setFont(font)
151 | self.link_label.setOpenExternalLinks(True)
152 | self.link_label.setObjectName("link_label")
153 | # Main menu bar, not used
154 | MainWindow.setCentralWidget(self.centralwidget)
155 | self.menubar = QtWidgets.QMenuBar(MainWindow)
156 | self.menubar.setGeometry(QtCore.QRect(0, 0, 827, 21))
157 | self.menubar.setObjectName("menubar")
158 | MainWindow.setMenuBar(self.menubar)
159 | self.statusbar = QtWidgets.QStatusBar(MainWindow)
160 | self.statusbar.setObjectName("statusbar")
161 | MainWindow.setStatusBar(self.statusbar)
162 | # Trainslations
163 | self.retranslateUi(MainWindow)
164 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
165 |
166 | def retranslateUi(self, MainWindow):
167 | """ Set the text and translations """
168 | _translate = QtCore.QCoreApplication.translate
169 | MainWindow.setWindowTitle(_translate("MainWindow", "YTPD.Beta"))
170 | self.title_label.setText(_translate("MainWindow", "YouTube Playlist Downloader"))
171 | self.enter_playlist_url_label.setText(_translate("MainWindow", "Enter playlist url:"))
172 | self.url_load_button.setText(_translate("MainWindow", "Load"))
173 | self.remove_from_list_button.setText(_translate("MainWindow", "Remove from list"))
174 | self.url_error_label.setText(_translate("MainWindow", "Could not retrieve data from url." +
175 | " Check the link and try again?"))
176 | self.url_fetching_data_label.setText(_translate("MainWindow", "Fetching data ... This might take a while."))
177 | self.download_folder_name_label.setText(_translate("MainWindow", "Download folder name:"))
178 | self.folder_name_button.setText(_translate("MainWindow", "Apply"))
179 | self.download_path_label.setText(_translate("MainWindow", "Download path: \n"))
180 | self.guide_label.setText(_translate("MainWindow", "Guide:\n" +
181 | "- Once the data from the url is loaded, video titles will be displayed" +
182 | " in the box to the right.\n" +
183 | "- By selecting a video title and pressing the Remove from list" +
184 | " button you can remove\n" +
185 | " the video from the download list.\n" +
186 | "- Then type in the folder name in which the files will be saved.\n" +
187 | "- The download path is set to the current path and" +
188 | " can not be changed.\n" +
189 | "- To download the files click Download. \n" +
190 | "- All video files are saved in a .mp4(only audio) format."))
191 | self.listWidget_label.setText(_translate("MainWindow", "Playlist videos:"))
192 | self.download_button.setText(_translate("MainWindow", "Download"))
193 | self.downloaded_label.setText(_translate("MainWindow", "Waiting for download :)"))
194 | self.credits_label.setText(_translate("MainWindow", "Created by: L.M.," +
195 | " source files available at: "))
196 | self.link_label.setText("'github.com/15minutOdmora/YouTubePlaylistDownloader'")
197 |
198 |
199 | if __name__ == "__main__":
200 | import sys
201 | app = QtWidgets.QApplication(sys.argv)
202 | MainWindow = QtWidgets.QMainWindow()
203 | ui = UiMainWindow()
204 | ui.setupUi(MainWindow)
205 | MainWindow.show()
206 | sys.exit(app.exec_())
207 |
--------------------------------------------------------------------------------
/ytpd_beta.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 827
10 | 498
11 |
12 |
13 |
14 |
15 | 75
16 | true
17 | false
18 |
19 |
20 |
21 | MainWindow
22 |
23 |
24 |
25 |
26 |
27 | 10
28 | 0
29 | 271
30 | 41
31 |
32 |
33 |
34 |
35 | Segoe UI Emoji
36 | 14
37 | 75
38 | true
39 | false
40 |
41 |
42 |
43 | YouTube Playlist Downloader
44 |
45 |
46 |
47 |
48 |
49 | 520
50 | 40
51 | 281
52 | 301
53 |
54 |
55 |
56 |
57 |
58 |
59 | 10
60 | 70
61 | 131
62 | 21
63 |
64 |
65 |
66 |
67 | 10
68 | 75
69 | true
70 | false
71 |
72 |
73 |
74 | Enter playlist url:
75 |
76 |
77 |
78 |
79 |
80 | 140
81 | 70
82 | 301
83 | 20
84 |
85 |
86 |
87 |
88 |
89 |
90 | 370
91 | 100
92 | 71
93 | 23
94 |
95 |
96 |
97 | Load
98 |
99 |
100 |
101 |
102 |
103 | 670
104 | 350
105 | 131
106 | 23
107 |
108 |
109 |
110 | Remove from list
111 |
112 |
113 |
114 |
115 |
116 | 10
117 | 390
118 | 481
119 | 16
120 |
121 |
122 |
123 | 24
124 |
125 |
126 |
127 |
128 |
129 | 140
130 | 50
131 | 271
132 | 16
133 |
134 |
135 |
136 |
137 | 50
138 | false
139 | false
140 |
141 |
142 |
143 | Could not retrieve data from url. Try again?
144 |
145 |
146 |
147 |
148 |
149 | 140
150 | 100
151 | 231
152 | 20
153 |
154 |
155 |
156 |
157 | 50
158 | false
159 | false
160 |
161 |
162 |
163 | Fetching data ... This might take a while.
164 |
165 |
166 |
167 |
168 |
169 | 10
170 | 280
171 | 161
172 | 21
173 |
174 |
175 |
176 |
177 | 10
178 |
179 |
180 |
181 | Download folder name:
182 |
183 |
184 |
185 |
186 |
187 | 170
188 | 280
189 | 211
190 | 20
191 |
192 |
193 |
194 |
195 |
196 |
197 | 390
198 | 280
199 | 61
200 | 23
201 |
202 |
203 |
204 | Apply
205 |
206 |
207 |
208 |
209 |
210 | 10
211 | 320
212 | 371
213 | 16
214 |
215 |
216 |
217 |
218 | 50
219 | false
220 | false
221 |
222 |
223 |
224 | Download path: c://neki/neki/neki
225 |
226 |
227 |
228 |
229 |
230 | 10
231 | 130
232 | 451
233 | 111
234 |
235 |
236 |
237 |
238 | 8
239 | 50
240 | false
241 | false
242 |
243 |
244 |
245 | Guide:
246 | - Once the data from the url is loaded, video titles will be displayed in the box to the right.
247 | - By selecting a video title and pressing the Remove from list button you can remove
248 | the vido from the download list.
249 | - Then type in the folder name in wich the files will be downloaded.
250 | - The download path is automaticly set to Desktop and can not be changed.
251 | - To download the files click Download.
252 |
253 |
254 |
255 |
256 |
257 | 520
258 | 20
259 | 201
260 | 16
261 |
262 |
263 |
264 |
265 | 50
266 | false
267 | false
268 |
269 |
270 |
271 | Playlist videos:
272 |
273 |
274 |
275 |
276 |
277 | 10
278 | 350
279 | 91
280 | 23
281 |
282 |
283 |
284 | Download
285 |
286 |
287 |
288 |
289 |
290 | 520
291 | 390
292 | 231
293 | 16
294 |
295 |
296 |
297 |
298 | 50
299 | false
300 | false
301 |
302 |
303 |
304 | Downloaded 0/max
305 |
306 |
307 |
308 |
309 |
310 | 10
311 | 420
312 | 581
313 | 16
314 |
315 |
316 |
317 |
318 | 50
319 | false
320 | false
321 |
322 |
323 |
324 | Created by: Liam Mislej, source files available at githublink
325 |
326 |
327 |
328 |
338 |
339 |
340 |
341 |
342 |
343 |
--------------------------------------------------------------------------------