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()