diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..3ffca5e
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,30 @@
+# https://hub.docker.com/r/library/python/tags/
+image: python:latest
+
+# Change pip's cache directory to be inside the project directory since we can
+# only cache local items.
+variables:
+ PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+
+# Pip's cache doesn't store the python packages
+# https://pip.pypa.io/en/stable/reference/pip_install/#caching
+#
+# If you want to also cache the installed packages, you have to install
+# them in a virtualenv and cache it as well.
+cache:
+ paths:
+ - .cache/pip
+ - venv/
+
+before_script:
+ - python -V # Print out python version for debugging
+
+test:pylint:
+ script:
+ - pip install pylint
+ - pylint -j 0 --exit-zero --ignored-classes=_socketobject *.py YouTubeMDBot
+
+test:
+ script:
+ - pip install -r YouTubeMDBot/requirements.txt
+ - python -m unittest $(pwd)/YouTubeMDBot/tests/*.py
diff --git a/Design/Database/database_model.mwb b/Design/Database/database_model.mwb
index 03ffdc9..e36a68a 100644
Binary files a/Design/Database/database_model.mwb and b/Design/Database/database_model.mwb differ
diff --git a/Design/Database/database_model.mwb.bak b/Design/Database/database_model.mwb.bak
index ea968e3..03ffdc9 100644
Binary files a/Design/Database/database_model.mwb.bak and b/Design/Database/database_model.mwb.bak differ
diff --git a/Design/Database/generated_sql_file.sql b/Design/Database/generated_sql_file.sql
index e65336e..7ced06a 100644
--- a/Design/Database/generated_sql_file.sql
+++ b/Design/Database/generated_sql_file.sql
@@ -1,5 +1,5 @@
-- MySQL Script generated by MySQL Workbench
--- lun 22 jul 2019 14:28:48 CEST
+-- jue 25 jul 2019 13:50:06 CEST
-- Model: New Model Version: 1.0
-- MySQL Workbench Forward Engineering
@@ -18,82 +18,6 @@ CREATE SCHEMA IF NOT EXISTS `youtubemd` DEFAULT CHARACTER SET utf8mb4 ;
SHOW WARNINGS;
USE `youtubemd` ;
--- -----------------------------------------------------
--- Table `youtubemd`.`User`
--- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS `youtubemd`.`User` (
- `id` INT(64) NOT NULL DEFAULT 0,
- `name` VARCHAR(45) NULL DEFAULT 'User',
- `surname` VARCHAR(45) NULL,
- `username` VARCHAR(45) NULL,
- `lastSeen` DATETIME NOT NULL,
- `firstUsage` DATETIME NOT NULL,
- PRIMARY KEY (`id`))
-ENGINE = InnoDB
-CHECKSUM = 1
-PACK_KEYS = 1;
-
-SHOW WARNINGS;
-CREATE UNIQUE INDEX `id_UNIQUE` ON `youtubemd`.`User` (`id` ASC) VISIBLE;
-
-SHOW WARNINGS;
-CREATE UNIQUE INDEX `username_UNIQUE` ON `youtubemd`.`User` (`username` ASC) VISIBLE;
-
-SHOW WARNINGS;
-
--- -----------------------------------------------------
--- Table `youtubemd`.`Preferences`
--- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS `youtubemd`.`Preferences` (
- `language` VARCHAR(3) NOT NULL DEFAULT 'en',
- `audioQuality` ENUM('320k', '256k', '128k') NOT NULL DEFAULT '128k',
- `audioSampling` ENUM('44000', '48000') NOT NULL DEFAULT '44000',
- `sendSongLinks` TINYINT NOT NULL DEFAULT 0,
- `User_id` INT(64) NOT NULL,
- PRIMARY KEY (`User_id`),
- CONSTRAINT `fk_Preferences_User`
- FOREIGN KEY (`User_id`)
- REFERENCES `youtubemd`.`User` (`id`)
- ON DELETE NO ACTION
- ON UPDATE NO ACTION)
-ENGINE = InnoDB
-CHECKSUM = 1;
-
-SHOW WARNINGS;
-
--- -----------------------------------------------------
--- Table `youtubemd`.`Metadata`
--- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS `youtubemd`.`Metadata` (
- `idMetadata` INT NOT NULL AUTO_INCREMENT,
- `title` VARCHAR(100) NOT NULL,
- `artist` VARCHAR(60) NOT NULL,
- `cover` BLOB NOT NULL,
- `duration` INT NULL,
- `customMetadata` TINYINT NOT NULL DEFAULT 0,
- PRIMARY KEY (`idMetadata`))
-ENGINE = InnoDB
-AUTO_INCREMENT = 0
-CHECKSUM = 1;
-
-SHOW WARNINGS;
-
--- -----------------------------------------------------
--- Table `youtubemd`.`VideoInformation`
--- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS `youtubemd`.`VideoInformation` (
- `id` VARCHAR(11) NOT NULL,
- `title` VARCHAR(100) NOT NULL,
- `channel` VARCHAR(60) NOT NULL,
- PRIMARY KEY (`id`))
-ENGINE = InnoDB
-CHECKSUM = 1;
-
-SHOW WARNINGS;
-CREATE UNIQUE INDEX `id_UNIQUE` ON `youtubemd`.`VideoInformation` (`id` ASC) VISIBLE;
-
-SHOW WARNINGS;
-
-- -----------------------------------------------------
-- Table `youtubemd`.`DownloadInformation`
-- -----------------------------------------------------
@@ -128,6 +52,23 @@ CREATE INDEX `fk_DownloadInformation_VideoInformation1_idx` ON `youtubemd`.`Down
SHOW WARNINGS;
+-- -----------------------------------------------------
+-- Table `youtubemd`.`DownloadStatistics`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`DownloadStatistics` (
+ `timesRequested` INT NOT NULL DEFAULT 0,
+ `DownloadInformation_file_id` VARCHAR(50) NOT NULL,
+ PRIMARY KEY (`DownloadInformation_file_id`),
+ CONSTRAINT `fk_DownloadStatistics_DownloadInformation1`
+ FOREIGN KEY (`DownloadInformation_file_id`)
+ REFERENCES `youtubemd`.`DownloadInformation` (`file_id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
-- -----------------------------------------------------
-- Table `youtubemd`.`History`
-- -----------------------------------------------------
@@ -154,35 +95,18 @@ CREATE INDEX `fk_History_DownloadInformation1_idx` ON `youtubemd`.`History` (`Do
SHOW WARNINGS;
-- -----------------------------------------------------
--- Table `youtubemd`.`VideoStatistics`
--- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS `youtubemd`.`VideoStatistics` (
- `timesRequested` INT NOT NULL DEFAULT 0,
- `VideoInformation_id` VARCHAR(11) NOT NULL,
- PRIMARY KEY (`VideoInformation_id`),
- CONSTRAINT `fk_VideoStatistics_VideoInformation1`
- FOREIGN KEY (`VideoInformation_id`)
- REFERENCES `youtubemd`.`VideoInformation` (`id`)
- ON DELETE NO ACTION
- ON UPDATE NO ACTION)
-ENGINE = InnoDB
-CHECKSUM = 1;
-
-SHOW WARNINGS;
-
--- -----------------------------------------------------
--- Table `youtubemd`.`DownloadStatistics`
+-- Table `youtubemd`.`Metadata`
-- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS `youtubemd`.`DownloadStatistics` (
- `timesRequested` INT NOT NULL DEFAULT 0,
- `DownloadInformation_file_id` VARCHAR(50) NOT NULL,
- PRIMARY KEY (`DownloadInformation_file_id`),
- CONSTRAINT `fk_DownloadStatistics_DownloadInformation1`
- FOREIGN KEY (`DownloadInformation_file_id`)
- REFERENCES `youtubemd`.`DownloadInformation` (`file_id`)
- ON DELETE NO ACTION
- ON UPDATE NO ACTION)
+CREATE TABLE IF NOT EXISTS `youtubemd`.`Metadata` (
+ `idMetadata` INT NOT NULL AUTO_INCREMENT,
+ `title` VARCHAR(100) NOT NULL,
+ `artist` VARCHAR(60) NOT NULL,
+ `cover` BLOB NOT NULL,
+ `duration` INT NULL,
+ `customMetadata` TINYINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (`idMetadata`))
ENGINE = InnoDB
+AUTO_INCREMENT = 0
CHECKSUM = 1;
SHOW WARNINGS;
@@ -246,6 +170,83 @@ CHECKSUM = 1;
SHOW WARNINGS;
+-- -----------------------------------------------------
+-- Table `youtubemd`.`Preferences`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`Preferences` (
+ `language` VARCHAR(3) NOT NULL DEFAULT 'en',
+ `audioQuality` ENUM('320k', '256k', '128k') NOT NULL DEFAULT '128k',
+ `audioSampling` ENUM('44000', '48000') NOT NULL DEFAULT '44000',
+ `sendSongLinks` TINYINT NOT NULL DEFAULT 0,
+ `askForMetadata` TINYINT NOT NULL DEFAULT 1,
+ `User_id` INT(64) NOT NULL,
+ PRIMARY KEY (`User_id`),
+ CONSTRAINT `fk_Preferences_User`
+ FOREIGN KEY (`User_id`)
+ REFERENCES `youtubemd`.`User` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`User`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`User` (
+ `id` INT(64) NOT NULL DEFAULT 0,
+ `name` VARCHAR(45) NULL DEFAULT 'User',
+ `surname` VARCHAR(45) NULL,
+ `username` VARCHAR(45) NULL,
+ `lastSeen` DATETIME NOT NULL,
+ `firstUsage` DATETIME NOT NULL,
+ PRIMARY KEY (`id`))
+ENGINE = InnoDB
+CHECKSUM = 1
+PACK_KEYS = 1;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `id_UNIQUE` ON `youtubemd`.`User` (`id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `username_UNIQUE` ON `youtubemd`.`User` (`username` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`VideoInformation`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`VideoInformation` (
+ `id` VARCHAR(11) NOT NULL,
+ `title` VARCHAR(100) NOT NULL,
+ `channel` VARCHAR(60) NOT NULL,
+ PRIMARY KEY (`id`))
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `id_UNIQUE` ON `youtubemd`.`VideoInformation` (`id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`VideoStatistics`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`VideoStatistics` (
+ `timesRequested` INT NOT NULL DEFAULT 0,
+ `VideoInformation_id` VARCHAR(11) NOT NULL,
+ PRIMARY KEY (`VideoInformation_id`),
+ CONSTRAINT `fk_VideoStatistics_VideoInformation1`
+ FOREIGN KEY (`VideoInformation_id`)
+ REFERENCES `youtubemd`.`VideoInformation` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
diff --git a/Design/Database/img_database_model.png b/Design/Database/img_database_model.png
index 6046766..8d061ce 100644
Binary files a/Design/Database/img_database_model.png and b/Design/Database/img_database_model.png differ
diff --git a/Design/Database/pdf_database_model.pdf b/Design/Database/pdf_database_model.pdf
index 4cdb445..7275783 100644
Binary files a/Design/Database/pdf_database_model.pdf and b/Design/Database/pdf_database_model.pdf differ
diff --git a/Design/Database/vect_database_model.svg b/Design/Database/vect_database_model.svg
index fe40b7a..d0785cd 100644
--- a/Design/Database/vect_database_model.svg
+++ b/Design/Database/vect_database_model.svg
@@ -30,187 +30,190 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -270,13 +273,13 @@
-
-
-
-
-
+
+
+
+
+
-
+
@@ -290,265 +293,134 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -562,168 +434,324 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -733,85 +761,85 @@
-
+
-
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -825,45 +853,45 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
@@ -872,89 +900,89 @@
-
-
-
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
+
+
+
@@ -963,13 +991,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -983,116 +1011,116 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
@@ -1100,10 +1128,10 @@
-
-
+
+
-
+
@@ -1112,38 +1140,38 @@
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -1157,186 +1185,186 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1352,34 +1380,34 @@
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1396,15 +1424,15 @@
-
+
-
+
-
-
+
+
@@ -1414,79 +1442,79 @@
-
+
-
+
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1502,90 +1530,90 @@
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1599,85 +1627,85 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1691,9 +1719,9 @@
-
-
-
+
+
+
@@ -1741,265 +1769,134 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -2013,240 +1910,396 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -2260,45 +2313,45 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
@@ -2307,89 +2360,89 @@
-
-
-
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
+
+
+
@@ -2398,13 +2451,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -2418,116 +2471,116 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
@@ -2535,10 +2588,10 @@
-
-
+
+
-
+
@@ -2547,38 +2600,38 @@
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -2592,186 +2645,186 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -2787,34 +2840,34 @@
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -2830,79 +2883,79 @@
-
+
-
+
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -2918,90 +2971,90 @@
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -3015,85 +3068,85 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/YouTubeMDBot/__init__.py b/YouTubeMDBot/__init__.py
new file mode 100644
index 0000000..d0c79d3
--- /dev/null
+++ b/YouTubeMDBot/__init__.py
@@ -0,0 +1,23 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from .bot import PROGRAM_ARGS
+from .bot import main
+
+from .logging_utils import LoggingHandler
+from .logging_utils import setup_logging
+
+from .decorators import send_action
+from .decorators import restricted
diff --git a/YouTubeMDBot/__main__.py b/YouTubeMDBot/__main__.py
new file mode 100644
index 0000000..8a858f1
--- /dev/null
+++ b/YouTubeMDBot/__main__.py
@@ -0,0 +1,15 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
diff --git a/YouTubeMDBot/bot.py b/YouTubeMDBot/bot.py
new file mode 100644
index 0000000..10b68d9
--- /dev/null
+++ b/YouTubeMDBot/bot.py
@@ -0,0 +1,37 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from telegram.ext import Updater
+from telegram.ext import CommandHandler
+
+PROGRAM_ARGS = None
+
+
+def main(args: dict):
+ global PROGRAM_ARGS
+ PROGRAM_ARGS = args
+ updater = Updater(args["token"], workers=args["workers"])
+
+ dispatcher = updater.dispatcher
+ dispatcher.add_handler(CommandHandler("hello", main))
+
+ if args["use_webhook"]:
+ updater.start_webhook(listen=args["ip"],
+ port=args["port"],
+ url_path=args["token"],
+ webhook_url=args["public_url"] + '/' + args["token"])
+ else:
+ updater.start_polling(args["poll_interval"])
+ updater.idle()
diff --git a/YouTubeMDBot/commands/StartHandler.py b/YouTubeMDBot/commands/StartHandler.py
new file mode 100644
index 0000000..d209a1e
--- /dev/null
+++ b/YouTubeMDBot/commands/StartHandler.py
@@ -0,0 +1,24 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from .. import LoggingHandler
+
+
+class StartHandler(object):
+ def __init__(self):
+ self._user_data = {}
+
+ def start(self, bot, update):
+ self._user_data[]
diff --git a/YouTubeMDBot/commands/__init__.py b/YouTubeMDBot/commands/__init__.py
new file mode 100644
index 0000000..8a858f1
--- /dev/null
+++ b/YouTubeMDBot/commands/__init__.py
@@ -0,0 +1,15 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
diff --git a/YouTubeMDBot/constants/__init__.py b/YouTubeMDBot/constants/__init__.py
new file mode 100644
index 0000000..1402386
--- /dev/null
+++ b/YouTubeMDBot/constants/__init__.py
@@ -0,0 +1,16 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from ..constants.app_constants import ydl_cli_options
diff --git a/YouTubeMDBot/constants/app_constants.py b/YouTubeMDBot/constants/app_constants.py
new file mode 100644
index 0000000..e1a7cb1
--- /dev/null
+++ b/YouTubeMDBot/constants/app_constants.py
@@ -0,0 +1,17 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+ydl_cli_options = ["youtube-dl", "--format", "bestaudio[ext=m4a]", "--quiet", "--output",
+ "-"]
diff --git a/YouTubeMDBot/decorators/__init__.py b/YouTubeMDBot/decorators/__init__.py
new file mode 100644
index 0000000..9a87cd2
--- /dev/null
+++ b/YouTubeMDBot/decorators/__init__.py
@@ -0,0 +1,17 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from ..decorators.decorators import restricted
+from ..decorators.decorators import send_action
diff --git a/YouTubeMDBot/decorators/decorators.py b/YouTubeMDBot/decorators/decorators.py
new file mode 100644
index 0000000..b378e01
--- /dev/null
+++ b/YouTubeMDBot/decorators/decorators.py
@@ -0,0 +1,54 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from functools import wraps
+
+from .. import PROGRAM_ARGS
+
+
+# logging = LoggingHandler()
+
+
+def send_action(action):
+ """
+ Sends an action while processing a command.
+ :param action: the action to be performed.
+ :return: itself.
+ """
+
+ def decorator(func):
+ @wraps(func)
+ def command_func(update, context, *args, **kwargs):
+ context.bot.send_chat_action(chat_id=update.effective_message.chat_id, action=action)
+ return func(update, context, *args, **kwargs)
+ return command_func
+
+ return decorator
+
+
+def restricted(func):
+ """
+ Restricts a specific function to be accessed from non-authorized users.
+ :param func: function to be wrapped.
+ :return: itself.
+ """
+ @wraps(func)
+ def wrapped(update, context, *args, **kwargs):
+ user_id = update.effective_user.id
+ if user_id not in PROGRAM_ARGS["admin"]:
+ logging.warning("Unauthorized access denied for {}.".format(user_id))
+ return
+ return func(update, context, *args, **kwargs)
+ return wrapped
diff --git a/YouTubeMDBot/downloader/__init__.py b/YouTubeMDBot/downloader/__init__.py
new file mode 100644
index 0000000..89c3cee
--- /dev/null
+++ b/YouTubeMDBot/downloader/__init__.py
@@ -0,0 +1,16 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from ..downloader.youtube_downloader import YouTubeDownloader
diff --git a/YouTubeMDBot/downloader/youtube_downloader.py b/YouTubeMDBot/downloader/youtube_downloader.py
new file mode 100644
index 0000000..8736255
--- /dev/null
+++ b/YouTubeMDBot/downloader/youtube_downloader.py
@@ -0,0 +1,43 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from io import BytesIO
+from typing import Tuple
+
+from ..constants.app_constants import ydl_cli_options
+
+
+class YouTubeDownloader(object):
+ def __init__(self, url: str):
+ self.__url: str = url
+ self.__options: list = ydl_cli_options.copy()
+ self.__options.append(self.__url)
+
+ def download(self) -> Tuple[BytesIO, bytes]:
+ import subprocess
+
+ proc = subprocess.Popen(self.__options,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = proc.communicate()
+ retcode = proc.returncode
+ if retcode == 0:
+ return BytesIO(stdout), stdout
+ else:
+ raise RuntimeError("youtube-dl downloader exception - more info: " +
+ str(stderr))
+
+ def get_url(self) -> str:
+ return self.__url
diff --git a/YouTubeMDBot/logging_utils/__init__.py b/YouTubeMDBot/logging_utils/__init__.py
new file mode 100644
index 0000000..45c2549
--- /dev/null
+++ b/YouTubeMDBot/logging_utils/__init__.py
@@ -0,0 +1,17 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+from ..logging_utils.utils import LoggingHandler
+from ..logging_utils.utils import setup_logging
diff --git a/YouTubeMDBot/logging_utils/utils.py b/YouTubeMDBot/logging_utils/utils.py
new file mode 100644
index 0000000..309a620
--- /dev/null
+++ b/YouTubeMDBot/logging_utils/utils.py
@@ -0,0 +1,116 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+import logging
+
+
+def cleanup_old_logs(log_file: str):
+ import tarfile
+ import os
+
+ tar_log_filename = log_file + ".tar.gz"
+
+ if os.path.exists(log_file):
+ if os.path.exists(tar_log_filename):
+ os.remove(tar_log_filename)
+ with tarfile.open(tar_log_filename, "w:gz") as tar:
+ tar.add(log_file, arcname=os.path.basename(log_file))
+ tar.close()
+ os.remove(log_file)
+
+
+def setup_logging(logger_name: str, log_file: str, level=logging.DEBUG,
+ formatter: str = "%(process)d - %(asctime)s | [%(levelname)s]: %(message)s"):
+ from os import path
+ from os import makedirs
+
+ log_dir = path.dirname(path.abspath(log_file))
+ if not path.exists(log_dir):
+ makedirs(log_dir)
+
+ cleanup_old_logs(log_file)
+ new_logging = logging.getLogger(logger_name)
+ logging_formatter = logging.Formatter(formatter)
+ logging_file_handler = logging.FileHandler(log_file, mode="w")
+
+ logging_file_handler.setFormatter(logging_formatter)
+
+ new_logging.setLevel(level)
+ new_logging.addHandler(logging_file_handler)
+
+ return logging_file_handler
+
+
+class LoggingHandler(object):
+ class __LoggingHandler(object):
+ def __init__(self, logs: list):
+ self.__logs = logs
+
+ def debug(self, msg):
+ for log in self.__logs:
+ log.debug(msg)
+
+ def info(self, msg):
+ for log in self.__logs:
+ log.info(msg)
+
+ def error(self, msg):
+ for log in self.__logs:
+ log.error(msg)
+
+ def warning(self, msg):
+ for log in self.__logs:
+ log.warning(msg)
+
+ def critical(self, msg):
+ for log in self.__logs:
+ log.critical(msg)
+
+ def get_loggers(self) -> list:
+ return self.__logs
+
+ __instance = None
+
+ def __new__(cls, *args, **kwargs):
+ if not LoggingHandler.__instance:
+ logs = kwargs.get("logs")
+ if not logs or len(logs) == 0:
+ raise AttributeError("At least kwarg \"log\" (a list of the loggers) must be provided")
+ LoggingHandler.__instance = LoggingHandler.__LoggingHandler(logs)
+ return LoggingHandler.__instance
+
+ def __getattr__(self, item):
+ return getattr(self.__instance, item)
+
+ def __setattr__(self, key, value):
+ return setattr(self.__instance, key, value)
+
+ def debug(self, msg):
+ self.__instance.debug(msg)
+
+ def info(self, msg):
+ self.__instance.info(msg)
+
+ def error(self, msg):
+ self.__instance.error(msg)
+
+ def warning(self, msg):
+ self.__instance.warning(msg)
+
+ def critical(self, msg):
+ self.__instance.critical(msg)
+
+ def get_loggers(self) -> list:
+ return self.__instance.get_loggers()
diff --git a/YouTubeMDBot/metadata/MetadataIdentifier.py b/YouTubeMDBot/metadata/MetadataIdentifier.py
new file mode 100644
index 0000000..b473a23
--- /dev/null
+++ b/YouTubeMDBot/metadata/MetadataIdentifier.py
@@ -0,0 +1,20 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+import acoustid
+
+
+class MetadataIdentifier(object):
+ def __init__(self, filename: str = None, audio: str = None):
diff --git a/YouTubeMDBot/metadata/__init__.py b/YouTubeMDBot/metadata/__init__.py
new file mode 100644
index 0000000..8a858f1
--- /dev/null
+++ b/YouTubeMDBot/metadata/__init__.py
@@ -0,0 +1,15 @@
+# YouTubeMDBot
+# Copyright (C) 2019 - Javinator9889
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
diff --git a/YouTubeMDBot/requirements.txt b/YouTubeMDBot/requirements.txt
new file mode 100644
index 0000000..9c3d398
--- /dev/null
+++ b/YouTubeMDBot/requirements.txt
@@ -0,0 +1,3 @@
+youtube_dl
+pyacoustid
+python-telegram-bot
diff --git a/YouTubeMDBot/tests/downloader.py b/YouTubeMDBot/tests/downloader.py
new file mode 100644
index 0000000..cb53f74
--- /dev/null
+++ b/YouTubeMDBot/tests/downloader.py
@@ -0,0 +1,46 @@
+import threading
+import unittest
+from time import sleep
+
+from YouTubeMDBot.downloader import YouTubeDownloader
+
+
+class DownloadTest(unittest.TestCase):
+ _elements = 0
+ _max = 0
+ _lock = threading.Lock()
+
+ def test_multithread_download(self):
+ yt1 = YouTubeDownloader(url="https://www.youtube.com/watch?v=Inm-N5rLUSI")
+ yt2 = YouTubeDownloader(url="https://www.youtube.com/watch?v=-_ZwpOdXXcA")
+ yt3 = YouTubeDownloader(url="https://www.youtube.com/watch?v=WOGWZD5iT10")
+ yt4 = YouTubeDownloader(url="https://www.youtube.com/watch?v=9HfoNUjw5u8")
+ t1 = threading.Thread(target=self.write_to_file, args=(yt1, "v1.m4a",))
+ t2 = threading.Thread(target=self.write_to_file, args=(yt2, "v2.m4a",))
+ t3 = threading.Thread(target=self.write_to_file, args=(yt3, "v3.m4a",))
+ t4 = threading.Thread(target=self.write_to_file, args=(yt4, "v4.m4a",))
+
+ self._max = 4
+
+ t1.start()
+ t2.start()
+ t3.start()
+ t4.start()
+
+ while self._elements < self._max:
+ sleep(1)
+
+ def barrier(self):
+ with self._lock:
+ self._elements += 1
+
+ def write_to_file(self, yt: YouTubeDownloader, name: str):
+ _, data = yt.download()
+ print(name + " downloaded")
+ with open(name, "wb") as f:
+ f.write(data)
+ self.barrier()
+
+
+if __name__ == '__main__':
+ unittest.main()