mirror of https://github.com/FreeTubeApp/FreeTube
Merge branch 'development'
This commit is contained in:
commit
142b30699a
|
@ -3,7 +3,7 @@
|
|||
"development": {
|
||||
"application/javascript": {
|
||||
"presets": [
|
||||
["env", { "targets": { "electron": "1.4" } }],
|
||||
["env", { "targets": { "electron-renderer": "1.4" } }],
|
||||
"react"
|
||||
],
|
||||
"plugins": ["transform-async-to-generator"],
|
||||
|
@ -13,7 +13,7 @@
|
|||
"production": {
|
||||
"application/javascript": {
|
||||
"presets": [
|
||||
["env", { "targets": { "electron": "1.4" } }],
|
||||
["env", { "targets": { "electron-renderer": "1.4" } }],
|
||||
"react"
|
||||
],
|
||||
"plugins": ["transform-async-to-generator"],
|
||||
|
|
|
@ -5,6 +5,7 @@ subscriptions\.db
|
|||
.vscode/
|
||||
.eslintrc*
|
||||
*.db
|
||||
*.code-workspace
|
||||
|
||||
electron-packager/win32-x64/FreeTube-win32-x64/
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"html": {
|
||||
"allowed_file_extensions": ["htm", "html", "xhtml", "shtml", "xml", "svg", "dust"],
|
||||
"brace_style": "collapse",
|
||||
"end_with_newline": true,
|
||||
"indent_char": " ",
|
||||
"indent_handlebars": true,
|
||||
"indent_inner_html": false,
|
||||
"indent_scripts": "keep",
|
||||
"indent_size": 4,
|
||||
"max_preserve_newlines": 10,
|
||||
"preserve_newlines": true,
|
||||
"unformatted": ["a", "span", "img", "code", "pre", "sub", "sup", "em", "strong", "b", "i", "u", "strike", "big", "small", "pre", "h1", "h2", "h3", "h4", "h5", "h6"], // List of tags that should not be reformatted
|
||||
"wrap_line_length": 0
|
||||
},
|
||||
"css": {
|
||||
"allowed_file_extensions": ["css", "scss", "sass", "less"],
|
||||
"end_with_newline": true,
|
||||
"indent_char": " ",
|
||||
"indent_size": 4,
|
||||
"newline_between_rules": true,
|
||||
"selector_separator": " ",
|
||||
"selector_separator_newline": true,
|
||||
"preserve_newlines": true,
|
||||
"max_preserve_newlines": 10
|
||||
},
|
||||
"js": {
|
||||
"allowed_file_extensions": ["js", "json", "jshintrc", "jsbeautifyrc"],
|
||||
"brace_style": "collapse",
|
||||
"break_chained_methods": false,
|
||||
"e4x": false,
|
||||
"end_with_newline": false,
|
||||
"indent_char": " ",
|
||||
"indent_level": 0,
|
||||
"indent_size": 4,
|
||||
"indent_with_tabs": false,
|
||||
"jslint_happy": true,
|
||||
"keep_array_indentation": false,
|
||||
"keep_function_indentation": false,
|
||||
"max_preserve_newlines": 0,
|
||||
"preserve_newlines": true,
|
||||
"space_after_anon_function": true,
|
||||
"space_before_conditional": true,
|
||||
"space_in_empty_paren": false,
|
||||
"space_in_paren": false,
|
||||
"unescape_strings": false,
|
||||
"wrap_line_length": 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"File": "Archivo",
|
||||
"Quit": "Salir",
|
||||
"Edit": "Editar",
|
||||
"Undo": "Deshacer",
|
||||
"Redo": "Rehacer",
|
||||
"Cut": "Cortar",
|
||||
"Copy": "Copiar",
|
||||
"Paste": "Pegar",
|
||||
"Delete": "Eliminar",
|
||||
"Select all": "Seleccionar todo",
|
||||
"View": "Ver",
|
||||
"Reload": "Recargar",
|
||||
"Force Reload": "Forzar Recarga",
|
||||
"Toggle Developer Tools": "Herramientas para desarrolladores",
|
||||
"Actual size": "Tamaño real",
|
||||
"Zoom in": "Aumentar zoom",
|
||||
"Zoom out": "Reducir zoom",
|
||||
"Toggle fullscreen": "Cambiar a pantalla completa",
|
||||
"Window": "Ventana",
|
||||
"Minimize": "Minimizar",
|
||||
"Close": "Cerrar",
|
||||
"FreeTube": "FreeTube",
|
||||
"Subscriptions": "Suscripciones",
|
||||
"Featured": "Destacados",
|
||||
"Most Popular": "Más Popular",
|
||||
"Saved": "Guardados",
|
||||
"Playlists": "Listas de Reproducción",
|
||||
"History": "Historial",
|
||||
"Settings": "Ajustes",
|
||||
"About": "Acerca de",
|
||||
"Search / Go to URL": "Buscar / Ir a la URL",
|
||||
"Search Results": "Resultados de búsqueda",
|
||||
"Subscriber": "Suscriptor",
|
||||
"Subscriber": "Suscriptores",
|
||||
"Video": "Vídeo",
|
||||
"Videos": "Vídeos",
|
||||
"View Full Playlist": "Ver Lista de Reproducción completa",
|
||||
"Live Now": "En vivo ahora",
|
||||
"Fetch more results": "Obtener más resultados",
|
||||
"Fetching results. Please wait": "Obteniendo más resultados. Por favor espere",
|
||||
"Latest Subscriptions": "Últimas Suscripciones",
|
||||
"Save Video": "Guardar Vídeo",
|
||||
"Remove Saved Video": "Eliminar Vídeo Guardado",
|
||||
"Open in YouTube": "Abrir en YouTube",
|
||||
"Copy YouTube Link": "Copiar Enlace a YouTube",
|
||||
"Open in HookTube": "Abrir en HookTube",
|
||||
"Copy HookTube Link": "Copiar Enlace a HookTube",
|
||||
"URL has been copied to the clipboard": "La URL ha sido copiada al portapapeles",
|
||||
"Found valid URL for 480p, but returned a 404. Video type might be available in the future.": "URL para 480p encontrada, pero devolvió un 404. El tipo de vídeo podría estar disponible en el futuro.",
|
||||
"Save": "Guardar",
|
||||
"Mini Player": "Mini Reproductor",
|
||||
"View": "Reproducción",
|
||||
"Views": "Reproducciones",
|
||||
"Subscribe": "Suscribirse",
|
||||
"Unsubscribe": "Desuscribirse",
|
||||
"Published on": "Publicado el",
|
||||
"Jan": "ene",
|
||||
"Feb": "feb",
|
||||
"Mar": "mar",
|
||||
"Apr": "abr",
|
||||
"May": "may",
|
||||
"Jun": "jun",
|
||||
"Jul": "jul",
|
||||
"Aug": "ago",
|
||||
"Sep": "sep",
|
||||
"Oct": "oct",
|
||||
"Nov": "nov",
|
||||
"Dec": "dic",
|
||||
"Show Comments": "Ver Comentarios",
|
||||
"Max of 100": "Máximo de 100",
|
||||
"Recommendations": "Recomendaciones",
|
||||
"Latest Subscriptions": "Últimas Suscripciones",
|
||||
"Getting Subscriptions. Please wait...": "Obteniendo Suscripciones. Por favor espere...",
|
||||
"Your Subscription list is currently empty. Start adding subscriptions to see them here.": "Tu lista de Suscripción está actualmente vacía. Empieza añadiendo suscripciones para verlas ahí.",
|
||||
"Saved Videos": "Videos Guardados",
|
||||
"Watch History": "Ver Historial",
|
||||
"API Key": "API Key",
|
||||
"Set API Key: Leave blank to use default": "Establecer API Key: Dejar en blanco para usar la de por defecto",
|
||||
"Use Dark Theme": "Usar Tema Oscuro",
|
||||
"Import Subscriptions": "Importar Suscripción",
|
||||
"Export Subscriptions": "Exportar Suscripción",
|
||||
"Clear History": "Limpiar Historial",
|
||||
"Are you sure you want to delete your history?": "¿Estás seguro de que quieres eliminar tu historial?",
|
||||
"Clear Saved Videos": "Limpiar Vídeos Guardados",
|
||||
"Are you sure you want to remove all saved videos?": "¿Estás seguro de que quieres eliminar todos los vídeos guardados?",
|
||||
"Clear Subscriptions": "Limpiar Suscripciones",
|
||||
"Are you sure you want to remove all subscriptions?": "¿Estás seguro de que quieres eliminar todas las suscripciones?",
|
||||
"Save Settings": "Guardar Ajustes",
|
||||
"Yes": "Sí",
|
||||
"No": "No",
|
||||
"Beta": "Beta",
|
||||
"This software is FOSS and released under the GNU Public License v3+.": "Este software es FOSS y liberado bajo la licencia GNU Public License v3+.",
|
||||
"Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.": "¿Encontraste un bug? ¿Quieres sugerir una nueva característica? ¿Quieres ayudar? Visita nuestra página de Github. Las contribuciones son bienvenidas"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "FreeTube",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -78,9 +78,7 @@
|
|||
"abab": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
||||
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
|
@ -163,6 +161,11 @@
|
|||
"integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-font": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-font/-/ansi-font-0.0.2.tgz",
|
||||
"integrity": "sha1-iQMBvVhBRi/TnAt3Ca/R9SUUMzE="
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
|
@ -355,6 +358,11 @@
|
|||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"array-equal": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
|
||||
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
|
||||
},
|
||||
"array-find-index": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
|
||||
|
@ -418,6 +426,11 @@
|
|||
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
|
||||
"dev": true
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
@ -1347,6 +1360,11 @@
|
|||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"browser-process-hrtime": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
|
||||
"integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44="
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
|
||||
|
@ -1712,6 +1730,63 @@
|
|||
"htmlparser2": "~3.8.1",
|
||||
"jsdom": "^7.0.2",
|
||||
"lodash": "^4.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cssstyle": {
|
||||
"version": "0.2.37",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
|
||||
"integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"cssom": "0.3.x"
|
||||
}
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-7.2.2.tgz",
|
||||
"integrity": "sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"abab": "^1.0.0",
|
||||
"acorn": "^2.4.0",
|
||||
"acorn-globals": "^1.0.4",
|
||||
"cssom": ">= 0.3.0 < 0.4.0",
|
||||
"cssstyle": ">= 0.2.29 < 0.3.0",
|
||||
"escodegen": "^1.6.1",
|
||||
"nwmatcher": ">= 1.3.7 < 2.0.0",
|
||||
"parse5": "^1.5.1",
|
||||
"request": "^2.55.0",
|
||||
"sax": "^1.1.4",
|
||||
"symbol-tree": ">= 3.1.0 < 4.0.0",
|
||||
"tough-cookie": "^2.2.0",
|
||||
"webidl-conversions": "^2.0.0",
|
||||
"whatwg-url-compat": "~0.6.5",
|
||||
"xml-name-validator": ">= 2.0.1 < 3.0.0"
|
||||
}
|
||||
},
|
||||
"parse5": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz",
|
||||
"integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz",
|
||||
"integrity": "sha1-O/glj30xjHRDw28uFpQCoaZwNQY=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"xml-name-validator": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz",
|
||||
"integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"chromium-pickle-js": {
|
||||
|
@ -1901,6 +1976,15 @@
|
|||
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==",
|
||||
"dev": true
|
||||
},
|
||||
"commonjs": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commonjs/-/commonjs-0.0.1.tgz",
|
||||
"integrity": "sha1-ZcUx3P9lZcp8ld38lmIricwClNU=",
|
||||
"requires": {
|
||||
"system": ">=0.0.1",
|
||||
"test": ">=0.0.5"
|
||||
}
|
||||
},
|
||||
"compare-version": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
|
||||
|
@ -2193,17 +2277,14 @@
|
|||
}
|
||||
},
|
||||
"cssom": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz",
|
||||
"integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=",
|
||||
"dev": true
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz",
|
||||
"integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog=="
|
||||
},
|
||||
"cssstyle": {
|
||||
"version": "0.2.37",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
|
||||
"integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz",
|
||||
"integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==",
|
||||
"requires": {
|
||||
"cssom": "0.3.x"
|
||||
}
|
||||
|
@ -2230,6 +2311,16 @@
|
|||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"data-urls": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.0.tgz",
|
||||
"integrity": "sha512-ai40PPQR0Fn1lD2PPie79CibnlMN2AYiDhwFX/rZHVsxbs5kNJSjegqXIprhouGXlRdEnfybva7kqRGnB6mypA==",
|
||||
"requires": {
|
||||
"abab": "^1.0.4",
|
||||
"whatwg-mimetype": "^2.0.0",
|
||||
"whatwg-url": "^6.4.0"
|
||||
}
|
||||
},
|
||||
"dateformat": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||
|
@ -2277,8 +2368,7 @@
|
|||
"deep-is": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
|
||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
|
||||
},
|
||||
"defaults": {
|
||||
"version": "1.0.3",
|
||||
|
@ -2417,6 +2507,14 @@
|
|||
"integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
|
||||
"dev": true
|
||||
},
|
||||
"domexception": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
|
||||
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
|
||||
"requires": {
|
||||
"webidl-conversions": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
|
||||
|
@ -4225,11 +4323,9 @@
|
|||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"escodegen": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz",
|
||||
"integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
|
||||
"integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
|
||||
"requires": {
|
||||
"esprima": "^3.1.3",
|
||||
"estraverse": "^4.2.0",
|
||||
|
@ -4241,15 +4337,12 @@
|
|||
"esprima": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
|
||||
"integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
|
@ -4262,15 +4355,12 @@
|
|||
"estraverse": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
|
||||
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
|
||||
},
|
||||
"events": {
|
||||
"version": "1.1.1",
|
||||
|
@ -4382,8 +4472,7 @@
|
|||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
||||
},
|
||||
"fd-slicer": {
|
||||
"version": "1.0.1",
|
||||
|
@ -5054,6 +5143,14 @@
|
|||
"integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=",
|
||||
"dev": true
|
||||
},
|
||||
"html-encoding-sniffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
|
||||
"integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
|
||||
"requires": {
|
||||
"whatwg-encoding": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"html-entities": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
|
||||
|
@ -5642,27 +5739,56 @@
|
|||
"optional": true
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-7.2.2.tgz",
|
||||
"integrity": "sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"version": "11.11.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.11.0.tgz",
|
||||
"integrity": "sha512-ou1VyfjwsSuWkudGxb03FotDajxAto6USAlmMZjE2lc0jCznt7sBWkhfRBRaWwbnmDqdMSTKTLT5d9sBFkkM7A==",
|
||||
"requires": {
|
||||
"abab": "^1.0.0",
|
||||
"acorn": "^2.4.0",
|
||||
"acorn-globals": "^1.0.4",
|
||||
"cssom": ">= 0.3.0 < 0.4.0",
|
||||
"cssstyle": ">= 0.2.29 < 0.3.0",
|
||||
"escodegen": "^1.6.1",
|
||||
"nwmatcher": ">= 1.3.7 < 2.0.0",
|
||||
"parse5": "^1.5.1",
|
||||
"request": "^2.55.0",
|
||||
"sax": "^1.1.4",
|
||||
"symbol-tree": ">= 3.1.0 < 4.0.0",
|
||||
"tough-cookie": "^2.2.0",
|
||||
"webidl-conversions": "^2.0.0",
|
||||
"whatwg-url-compat": "~0.6.5",
|
||||
"xml-name-validator": ">= 2.0.1 < 3.0.0"
|
||||
"abab": "^1.0.4",
|
||||
"acorn": "^5.3.0",
|
||||
"acorn-globals": "^4.1.0",
|
||||
"array-equal": "^1.0.0",
|
||||
"cssom": ">= 0.3.2 < 0.4.0",
|
||||
"cssstyle": ">= 0.3.1 < 0.4.0",
|
||||
"data-urls": "^1.0.0",
|
||||
"domexception": "^1.0.0",
|
||||
"escodegen": "^1.9.0",
|
||||
"html-encoding-sniffer": "^1.0.2",
|
||||
"left-pad": "^1.2.0",
|
||||
"nwsapi": "^2.0.0",
|
||||
"parse5": "4.0.0",
|
||||
"pn": "^1.1.0",
|
||||
"request": "^2.83.0",
|
||||
"request-promise-native": "^1.0.5",
|
||||
"sax": "^1.2.4",
|
||||
"symbol-tree": "^3.2.2",
|
||||
"tough-cookie": "^2.3.3",
|
||||
"w3c-hr-time": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2",
|
||||
"whatwg-encoding": "^1.0.3",
|
||||
"whatwg-mimetype": "^2.1.0",
|
||||
"whatwg-url": "^6.4.1",
|
||||
"ws": "^4.0.0",
|
||||
"xml-name-validator": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
|
||||
"integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz",
|
||||
"integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==",
|
||||
"requires": {
|
||||
"acorn": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsesc": {
|
||||
|
@ -5817,6 +5943,11 @@
|
|||
"invert-kv": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"left-pad": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
|
||||
"integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="
|
||||
},
|
||||
"less": {
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz",
|
||||
|
@ -6005,7 +6136,6 @@
|
|||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
|
||||
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prelude-ls": "~1.1.2",
|
||||
"type-check": "~0.3.2"
|
||||
|
@ -6060,8 +6190,7 @@
|
|||
"lodash": {
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
},
|
||||
"lodash._reinterpolate": {
|
||||
"version": "3.0.0",
|
||||
|
@ -6135,6 +6264,11 @@
|
|||
"integrity": "sha1-YAYMxr1iW01FZ+wn3EXNG+nuwBI=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.sortby": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
|
||||
},
|
||||
"lodash.template": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
|
||||
|
@ -6619,6 +6753,11 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nwsapi": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.7.tgz",
|
||||
"integrity": "sha512-VZXniaaaORAXGCNsvUNefsKRQYk8zCzQZ57jalgrpHcU70OrAzKAiN/3plYtH/VPRmZeYyUzQiYfKzcMXC1g5Q=="
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
|
||||
|
@ -6706,7 +6845,6 @@
|
|||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
|
||||
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"deep-is": "~0.1.3",
|
||||
"fast-levenshtein": "~2.0.4",
|
||||
|
@ -6885,11 +7023,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"parse5": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz",
|
||||
"integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
|
||||
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
|
||||
},
|
||||
"parser-toolkit": {
|
||||
"version": "0.0.5",
|
||||
|
@ -6986,6 +7122,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"pn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
|
||||
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
|
||||
},
|
||||
"postcss": {
|
||||
"version": "5.2.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
|
||||
|
@ -7301,8 +7442,7 @@
|
|||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
|
||||
},
|
||||
"prepend-http": {
|
||||
"version": "1.0.4",
|
||||
|
@ -7683,6 +7823,24 @@
|
|||
"uuid": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
|
||||
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
|
||||
"requires": {
|
||||
"lodash": "^4.13.1"
|
||||
}
|
||||
},
|
||||
"request-promise-native": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
|
||||
"integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
|
||||
"requires": {
|
||||
"request-promise-core": "1.1.1",
|
||||
"stealthy-require": "^1.1.0",
|
||||
"tough-cookie": ">=2.3.3"
|
||||
}
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
@ -8147,6 +8305,11 @@
|
|||
"integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=",
|
||||
"dev": true
|
||||
},
|
||||
"stealthy-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
|
||||
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
|
||||
},
|
||||
"stream-json": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-json/-/stream-json-0.6.1.tgz",
|
||||
|
@ -8368,9 +8531,7 @@
|
|||
"symbol-tree": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
|
||||
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY="
|
||||
},
|
||||
"synchronous-promise": {
|
||||
"version": "1.0.18",
|
||||
|
@ -8378,6 +8539,11 @@
|
|||
"integrity": "sha512-UqMAK6BBBXu8qaDI5omlyV9iDpM9nQfgthaBOK0nlfXnMgiuOBv+meWG73CGeCCFRhOOOa2e4rvqYzfynzy5zg==",
|
||||
"dev": true
|
||||
},
|
||||
"system": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/system/-/system-2.0.1.tgz",
|
||||
"integrity": "sha512-BwSUSa8LMHZouGadZ34ck3TsrH5s3oMmTKPK+xHdbBnTCZOZMJ38fHGKLAHkBl0PXru1Z4BsymQU4qqvTxWzdQ=="
|
||||
},
|
||||
"tabtab": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tabtab/-/tabtab-2.2.2.tgz",
|
||||
|
@ -8596,6 +8762,14 @@
|
|||
"lazy-val": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/test/-/test-0.6.0.tgz",
|
||||
"integrity": "sha1-WYasRF7Bd1QyJRLRBLoyyKY+k44=",
|
||||
"requires": {
|
||||
"ansi-font": "0.0.2"
|
||||
}
|
||||
},
|
||||
"thenify": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
|
||||
|
@ -8759,11 +8933,19 @@
|
|||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
|
||||
"integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"transformers": {
|
||||
"version": "2.1.0",
|
||||
|
@ -8865,7 +9047,6 @@
|
|||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
|
||||
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prelude-ls": "~1.1.2"
|
||||
}
|
||||
|
@ -9102,6 +9283,14 @@
|
|||
"integrity": "sha512-x3LV3wdmmERhVCYy3quqA57NJW7F3i6faas++pJQWtknWT+n7k30F4TVdHvCLn48peTJFRvCpxs3UuFPqgeELg==",
|
||||
"dev": true
|
||||
},
|
||||
"w3c-hr-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
|
||||
"integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
|
||||
"requires": {
|
||||
"browser-process-hrtime": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"wcwidth": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
|
@ -9112,11 +9301,39 @@
|
|||
}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz",
|
||||
"integrity": "sha1-O/glj30xjHRDw28uFpQCoaZwNQY=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
|
||||
},
|
||||
"whatwg-encoding": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz",
|
||||
"integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==",
|
||||
"requires": {
|
||||
"iconv-lite": "0.4.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
|
||||
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"whatwg-mimetype": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz",
|
||||
"integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew=="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
|
||||
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
|
||||
"requires": {
|
||||
"lodash.sortby": "^4.7.0",
|
||||
"tr46": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"whatwg-url-compat": {
|
||||
"version": "0.6.5",
|
||||
|
@ -9126,6 +9343,15 @@
|
|||
"optional": true,
|
||||
"requires": {
|
||||
"tr46": "~0.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"whet.extend": {
|
||||
|
@ -9190,8 +9416,7 @@
|
|||
"wordwrap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
|
||||
"dev": true
|
||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "2.1.0",
|
||||
|
@ -9214,12 +9439,19 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
|
||||
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0",
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"xml-name-validator": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz",
|
||||
"integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
|
||||
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.17",
|
||||
|
|
189
package.json
189
package.json
|
@ -1,95 +1,102 @@
|
|||
{
|
||||
"name": "FreeTube",
|
||||
"productName": "FreeTube",
|
||||
"version": "0.3.1",
|
||||
"description": "An Open Source YouTube app for privacy.",
|
||||
"main": "src/js/init.js",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"make:mac": "electron-forge make --platform=darwin",
|
||||
"make:linux": "electron-forge make --platform=linux",
|
||||
"make:linux:zip": "electron-forge make --platform=linux --targets=zip",
|
||||
"make:deb": "electron-forge make --platform=linux --targets=deb",
|
||||
"make:rpm": "electron-forge make --platform=linux --targets=rpm",
|
||||
"make:snap": "electron-forge package && electron-installer-snap --src=out/FreeTube-linux-x64",
|
||||
"make:flatpak": "electron-installer-flatpak --src out/FreeTube-linux-x64/ --dest out/make --arch x64",
|
||||
"make:appimage": "electron-forge make --platform=linux --targets=electron-forge-maker-appimage",
|
||||
"make:win": "electron-forge make --platform=win32",
|
||||
"make:win:zip": "electron-forge make --platform=win32 --targets=zip"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
"name": "PrestonN",
|
||||
"email": "FreeTubeApp@protonmail.com",
|
||||
"url": "https://github.com/FreeTubeApp/FreeTube"
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"config": {
|
||||
"forge": {
|
||||
"make_targets": {
|
||||
"win32": [
|
||||
"squirrel",
|
||||
"zip"
|
||||
],
|
||||
"darwin": [
|
||||
"zip"
|
||||
],
|
||||
"linux": [
|
||||
"deb",
|
||||
"rpm",
|
||||
"electron-forge-maker-appimage",
|
||||
"zip"
|
||||
]
|
||||
},
|
||||
"protocols": [
|
||||
{
|
||||
"name": "freetube",
|
||||
"role": "Viewer",
|
||||
"schemes": [
|
||||
"freetube"
|
||||
]
|
||||
}
|
||||
],
|
||||
"electronPackagerConfig": {
|
||||
"packageManager": "yarn",
|
||||
"icon": "./src/icons/iconColor.icns"
|
||||
},
|
||||
"electronWinstallerConfig": {
|
||||
"name": "freetube",
|
||||
"iconUrl": "https://raw.githubusercontent.com/FreeTubeApp/FreeTubeApp.github.io/master/images/iconColor.ico",
|
||||
"setupIcon": "./src/icons/iconColor.ico"
|
||||
},
|
||||
"electronInstallerDebian": {
|
||||
"icon": "src/icons/iconColor.png"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"name": "FreeTube",
|
||||
"productName": "FreeTube",
|
||||
"version": "0.3.2",
|
||||
"description": "An Open Source YouTube app for privacy.",
|
||||
"main": "src/js/init.js",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"make:all": "npm run make:mac && npm run make:linux:x86 && npm run make:linux:arm && npm run make:win",
|
||||
"make:mac": "electron-forge make --platform=darwin",
|
||||
"make:win": "electron-forge make --platform=win32",
|
||||
"make:win:zip": "electron-forge make --platform=win32 --targets=zip",
|
||||
"make:linux:x86": "electron-forge make --platform=linux --arch x64",
|
||||
"make:linux:x86:zip": "electron-forge make --platform=linux --targets=zip --arch x64",
|
||||
"make:linux:x86:deb": "electron-forge make --platform=linux --targets=deb --arch x64",
|
||||
"make:linux:x86:rpm": "electron-forge make --platform=linux --targets=rpm --arch x64",
|
||||
"make:linux:x86:snap": "electron-forge package && electron-installer-snap --src=out/FreeTube-linux-x64 --arch x64",
|
||||
"make:linux:x86:flatpak": "electron-installer-flatpak --src out/FreeTube-linux-x64/ --dest out/make --arch x64",
|
||||
"make:linux:x86:appimage": "electron-forge make --platform=linux --targets=electron-forge-maker-appimage --arch x64",
|
||||
"make:linux:arm": "electron-forge make --platform=linux --arch arm64",
|
||||
"make:linux:arm:zip": "electron-forge make --platform=linux --targets=zip --arch arm64",
|
||||
"make:linux:arm:deb": "electron-forge make --platform=linux --targets=deb --arch arm64",
|
||||
"make:linux:arm:rpm": "electron-forge make --platform=linux --targets=rpm --arch arm64",
|
||||
"make:linux:arm:appimage": "electron-forge make --platform=linux --targets=electron-forge-maker-appimage --arch arm64"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
"name": "PrestonN",
|
||||
"email": "FreeTubeApp@protonmail.com",
|
||||
"url": "https://github.com/FreeTubeApp/FreeTube"
|
||||
}
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"config": {
|
||||
"forge": {
|
||||
"make_targets": {
|
||||
"win32": [
|
||||
"squirrel"
|
||||
],
|
||||
"darwin": [
|
||||
"zip"
|
||||
],
|
||||
"linux": [
|
||||
"deb",
|
||||
"rpm",
|
||||
"electron-forge-maker-appimage",
|
||||
"zip"
|
||||
]
|
||||
},
|
||||
"protocols": [
|
||||
{
|
||||
"name": "freetube",
|
||||
"role": "Viewer",
|
||||
"schemes": [
|
||||
"freetube"
|
||||
]
|
||||
}
|
||||
],
|
||||
"electronPackagerConfig": {
|
||||
"packageManager": "yarn",
|
||||
"icon": "./src/icons/iconColor.icns"
|
||||
},
|
||||
"electronWinstallerConfig": {
|
||||
"name": "freetube",
|
||||
"iconUrl": "https://raw.githubusercontent.com/FreeTubeApp/FreeTubeApp.github.io/master/images/iconColor.ico",
|
||||
"setupIcon": "./src/icons/iconColor.ico"
|
||||
},
|
||||
"electronInstallerDebian": {
|
||||
"icon": "src/icons/iconColor.png"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/FreeTubeApp/FreeTube"
|
||||
}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron-forge": "^5.2.2",
|
||||
"electron-forge-maker-appimage": "^20.14.4",
|
||||
"electron-installer-flatpak": "^0.8.0",
|
||||
"electron-installer-snap": "^2.0.1",
|
||||
"electron-prebuilt-compile": "2.0.2",
|
||||
"electron-winstaller": "^2.6.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"autolinker": "^1.6.2",
|
||||
"commonjs": "0.0.1",
|
||||
"dateformat": "^3.0.3",
|
||||
"electron-compile": "6.4.2",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"github-version-checker": "^2.0.1",
|
||||
"jquery": "^3.3.1",
|
||||
"jsdom": "^11.11.0",
|
||||
"mustache": "^2.3.0",
|
||||
"nedb": "^1.8.0",
|
||||
"opml-to-json": "0.0.3",
|
||||
"tor-request": "^2.1.2",
|
||||
"ytdl-core": "^0.20.4"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron-forge": "^5.2.2",
|
||||
"electron-forge-maker-appimage": "^20.14.4",
|
||||
"electron-installer-flatpak": "^0.8.0",
|
||||
"electron-installer-snap": "^2.0.1",
|
||||
"electron-prebuilt-compile": "2.0.2",
|
||||
"electron-winstaller": "^2.6.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"autolinker": "^1.6.2",
|
||||
"dateformat": "^3.0.3",
|
||||
"electron-compile": "6.4.2",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"github-version-checker": "^2.0.1",
|
||||
"jquery": "^3.3.1",
|
||||
"mustache": "^2.3.0",
|
||||
"nedb": "^1.8.0",
|
||||
"opml-to-json": "0.0.3",
|
||||
"tor-request": "^2.1.2",
|
||||
"ytdl-core": "^0.20.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,22 +11,11 @@
|
|||
<link rel="stylesheet" href="style/loading.css">
|
||||
<link rel="stylesheet" href="style/fa-solid.min.css">
|
||||
<link rel="stylesheet" href="style/fontawesome-all.min.css">
|
||||
<script src="js/youtubeApi.js"></script>
|
||||
<script src="js/settings.js"></script>
|
||||
<script src="js/updates.js"></script>
|
||||
<script src="js/layout.js"></script>
|
||||
<script src="js/videos.js"></script>
|
||||
<script src="js/player.js"></script>
|
||||
<script src="js/subscriptions.js"></script>
|
||||
<script src="js/channels.js"></script>
|
||||
<script src="js/savedVideos.js"></script>
|
||||
<script src="js/history.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<title>Freetube Player</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='loading'>
|
||||
<div v-if='seen' id='loading'>
|
||||
<div class="spinner">
|
||||
<div class="double-bounce1"></div>
|
||||
<div class="double-bounce2"></div>
|
||||
|
@ -47,23 +36,22 @@
|
|||
<input id='search' class="search" type="text" placeholder="Search / Go to URL">
|
||||
<i onclick='parseSearchText()' class="fas fa-search searchButton" style='margin-right: -10px; cursor: pointer'></i>
|
||||
</div>
|
||||
<img src='icons/iconBlack.png' id='menuIcon'/>
|
||||
|
||||
<img src='icons/textBlack.png' id='menuText'/>
|
||||
<img src='icons/iconBlack.png' id='menuIcon' />
|
||||
<img src='icons/textBlack.png' id='menuText' />
|
||||
</div>
|
||||
<div id='sideNavDisabled'></div>
|
||||
<div id="sideNav">
|
||||
<div class="sideNavContainer">
|
||||
<ul>
|
||||
<li onclick='loadSubscriptions()'><i class="fas fa-rss"></i> Subscriptions</li>
|
||||
<li onclick='showMostPopular()'><i class="fas fa-users"></i> Most Popular</li>
|
||||
<li onclick='showSavedVideos()'><i class="fas fa-star"></i> Saved</li>
|
||||
<li onclick='showHistory()'><i class="fas fa-history"></i> History</li>
|
||||
<li v-on:click='subscriptions'><i class="fas fa-rss"></i> Subscriptions</li>
|
||||
<li v-on:click='popular'><i class="fas fa-users"></i> Most Popular</li>
|
||||
<li v-on:click='saved'><i class="fas fa-star"></i> Favorites</li>
|
||||
<li v-on:click='history'><i class="fas fa-history"></i> History</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<ul>
|
||||
<li onclick='showSettings()'><i class="fas fa-sliders-h"></i> Settings</li>
|
||||
<li onclick='showAbout()'><i class="fas fa-info-circle"></i> About</li>
|
||||
<li v-on:click='settings'><i class="fas fa-sliders-h"></i> Settings</li>
|
||||
<li v-on:click='about'><i class="fas fa-info-circle"></i> About</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<ul id='subscriptions'>
|
||||
|
@ -71,7 +59,41 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id='noSubscriptions' v-if='seen'>
|
||||
<h2 class="message">
|
||||
Your Subscription list is currently empty. Start adding subscriptions
|
||||
to see them here.
|
||||
<br /><br />
|
||||
<i class="far fa-frown" style="font-size: 200px"></i>
|
||||
</h2>
|
||||
</div>
|
||||
<div id='channelView'></div>
|
||||
<div id='mainHeaderView'></div>
|
||||
<div id='channelVideosView'></div>
|
||||
<div id='searchView'></div>
|
||||
<div id='subscriptionView'></div>
|
||||
<div id='popularView'></div>
|
||||
<div id='savedView'></div>
|
||||
<div id='historyView'></div>
|
||||
<div id='aboutView'></div>
|
||||
<div id='settingsView'></div>
|
||||
<div id='playerView'></div>
|
||||
</div>
|
||||
<div id='progressView'></div>
|
||||
</body>
|
||||
<script src="js/general.js"></script>
|
||||
<script src="js/youtubeApi.js"></script>
|
||||
<script src="js/updates.js"></script>
|
||||
<script src="js/db.js"></script>
|
||||
<script src="js/settings.js"></script>
|
||||
<script src="js/layout.js"></script>
|
||||
<script src="js/templates.js"></script>
|
||||
<script src="js/videos.js"></script>
|
||||
<script src="js/player.js"></script>
|
||||
<script src="js/subscriptions.js"></script>
|
||||
<script src="js/channels.js"></script>
|
||||
<script src="js/savedVideos.js"></script>
|
||||
<script src="js/history.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -1,75 +1,58 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* File for all functions related specifically for channels.
|
||||
*/
|
||||
|
||||
/*function getChannelThumbnail(channelId, callback) {
|
||||
let url = '';
|
||||
|
||||
youtubeAPI('channels', {
|
||||
'id': channelId,
|
||||
'part': 'snippet',
|
||||
}, function (data){
|
||||
callback(data.items[0].snippet.thumbnails.high.url);
|
||||
});
|
||||
}*/
|
||||
* File for all functions related specifically for channels.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Display a channel page, showing latest uploads.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to display.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
* Display a channel page, showing latest uploads.
|
||||
*
|
||||
* @param {string} channelId - The channel ID to display.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function goToChannel(channelId) {
|
||||
event.stopPropagation();
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
|
||||
let subButtonText;
|
||||
headerView.title = 'Latest Uploads';
|
||||
hideViews();
|
||||
loadingView.seen = true;
|
||||
|
||||
// Setting subButtonText here as Mustache templates are logic-less.
|
||||
isSubscribed(channelId).then((subscribed) => {
|
||||
subButtonText = (subscribed ? "UNSUBSCRIBE" : "SUBSCRIBE");
|
||||
channelView.subButtonText = (subscribed ? "UNSUBSCRIBE" : "SUBSCRIBE");
|
||||
});
|
||||
|
||||
// Grab general channel information
|
||||
youtubeAPI('channels', {
|
||||
part: 'snippet,brandingSettings,statistics',
|
||||
id: channelId,
|
||||
}, function (data){
|
||||
}, (data) => {
|
||||
const channelData = data.items[0];
|
||||
|
||||
const channelViewTemplate = require('./templates/channelView.html');
|
||||
mustache.parse(channelViewTemplate);
|
||||
const rendered = mustache.render(channelViewTemplate, {
|
||||
channelId: channelId,
|
||||
channelName: channelData.brandingSettings.channel.title,
|
||||
channelBanner: channelData.brandingSettings.image.bannerImageUrl,
|
||||
channelImage: channelData.snippet.thumbnails.high.url,
|
||||
subCount: channelData.statistics.subscriberCount.toLocaleString(), //toLocaleString adds commas as thousands separators
|
||||
channelDescription: autolinker.link(channelData.brandingSettings.channel.description), //autolinker makes URLs clickable
|
||||
subButtonText: subButtonText,
|
||||
});
|
||||
$('#main').html(rendered);
|
||||
stopLoadingAnimation();
|
||||
channelView.id = channelId;
|
||||
channelView.name = channelData.brandingSettings.channel.title;
|
||||
channelView.banner = channelData.brandingSettings.image.bannerImageUrl;
|
||||
channelView.icon = channelData.snippet.thumbnails.high.url;
|
||||
channelView.subCount = channelData.statistics.subscriberCount.toLocaleString(); //toLocaleString adds commas as thousands separators
|
||||
channelView.description = autolinker.link(channelData.brandingSettings.channel.description); //autolinker makes URLs clickable
|
||||
|
||||
|
||||
// Grab the channel's latest uploads. API forces a max of 50.
|
||||
youtubeAPI('search', {
|
||||
|
@ -79,12 +62,40 @@ function goToChannel(channelId) {
|
|||
maxResults: 50,
|
||||
order: 'date',
|
||||
}, function (data) {
|
||||
// Display recent uploads to #main
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
channelVideosView.videoList = [];
|
||||
|
||||
if (subscriptionView.seen === false && aboutView.seen === false && headerView.seen === false && searchView.seen === false && settingsView.seen === false && popularView.seen === false && savedView.seen === false && historyView.seen === false) {
|
||||
channelVideosView.seen = true;
|
||||
channelView.seen = true;
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
|
||||
loadingView.seen = false;
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video);
|
||||
displayVideo(video, 'channel');
|
||||
});
|
||||
|
||||
// Grab the channel's latest uploads. API forces a max of 50.
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet',
|
||||
channelId: channelId,
|
||||
type: 'video',
|
||||
maxResults: 50,
|
||||
order: 'date',
|
||||
}, function (data) {
|
||||
// Display recent uploads to #main
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const electron = require('electron');
|
||||
const Datastore = require('nedb'); // database logic
|
||||
const localDataStorage = electron.remote.app.getPath('userData'); // Grabs the userdata directory based on the user's OS
|
||||
|
||||
const subDb = new Datastore({
|
||||
filename: localDataStorage + '/subscriptions.db',
|
||||
autoload: true
|
||||
});
|
||||
|
||||
const historyDb = new Datastore({
|
||||
filename: localDataStorage + '/videohistory.db',
|
||||
autoload: true
|
||||
});
|
||||
|
||||
const savedVidsDb = new Datastore({
|
||||
filename: localDataStorage + '/savedvideos.db',
|
||||
autoload: true
|
||||
});
|
||||
|
||||
const settingsDb = new Datastore({
|
||||
filename: localDataStorage + '/settings.db',
|
||||
autoload: true
|
||||
});
|
357
src/js/events.js
357
src/js/events.js
|
@ -1,22 +1,20 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube is free software: you can redistribute it and/or modify
|
||||
sit 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* File for events within application. Work needs to be done throughout the application
|
||||
* to use this style more. Please use this style going forward if possible.
|
||||
|
@ -26,185 +24,179 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
* Event when user clicks comment box,
|
||||
* and wants to show/display comments for the user.
|
||||
*/
|
||||
let showComments = function(event) {
|
||||
let comments = $('#comments');
|
||||
let showComments = function (event) {
|
||||
let comments = $('#comments');
|
||||
|
||||
if (comments.css('display') === 'none') {
|
||||
comments.attr('loaded', 'true');
|
||||
if (comments.css('display') === 'none') {
|
||||
comments.attr('loaded', 'true');
|
||||
|
||||
youtubeAPI('commentThreads', {
|
||||
'videoId': $('#comments').attr('data-video-id'),
|
||||
'part': 'snippet,replies',
|
||||
'maxResults': 100,
|
||||
}, function (data){
|
||||
let comments = [];
|
||||
let items = data.items;
|
||||
youtubeAPI('commentThreads', {
|
||||
'videoId': $('#comments').attr('data-video-id'),
|
||||
'part': 'snippet,replies',
|
||||
'maxResults': 100,
|
||||
}, function (data) {
|
||||
let comments = [];
|
||||
let items = data.items;
|
||||
|
||||
items.forEach((object) => {
|
||||
let snippet = object.snippet.topLevelComment.snippet;
|
||||
items.forEach((object) => {
|
||||
let snippet = object.snippet.topLevelComment.snippet;
|
||||
|
||||
snippet.publishedAt = dateFormat(new Date(snippet.publishedAt), "mmm dS, yyyy");
|
||||
snippet.publishedAt = dateFormat(new Date(snippet.publishedAt), "mmm dS, yyyy");
|
||||
|
||||
comments.push(snippet);
|
||||
})
|
||||
const commentsTemplate = require('./templates/comments.html');
|
||||
const html = mustache.render(commentsTemplate, {
|
||||
comments: comments,
|
||||
});
|
||||
$('#comments').html(html);
|
||||
});
|
||||
comments.push(snippet);
|
||||
})
|
||||
const commentsTemplate = require('./templates/comments.html');
|
||||
const html = mustache.render(commentsTemplate, {
|
||||
comments: comments,
|
||||
});
|
||||
$('#comments').html(html);
|
||||
});
|
||||
|
||||
comments.show();
|
||||
} else {
|
||||
comments.hide();
|
||||
}
|
||||
comments.show();
|
||||
} else {
|
||||
comments.hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Play / Pause the video player upon click.
|
||||
*/
|
||||
let playPauseVideo = function(event) {
|
||||
let el = event.currentTarget;
|
||||
el.paused ? el.play() : el.pause();
|
||||
let playPauseVideo = function (event) {
|
||||
let el = event.currentTarget;
|
||||
el.paused ? el.play() : el.pause();
|
||||
};
|
||||
|
||||
$('.videoPlayer').keypress((event) => {
|
||||
console.log(event.which);
|
||||
});
|
||||
|
||||
let videoShortcutHandler = function(event) {
|
||||
console.log(event.which);
|
||||
let videoPlayer = $('.videoPlayer').get(0);
|
||||
if (typeof(videoPlayer) !== 'undefined' && !$('#jumpToInput').is(':focus') && !$('#search').is(':focus')){
|
||||
switch (event.which) {
|
||||
case 32:
|
||||
// Space Bar
|
||||
event.preventDefault();
|
||||
videoPlayer.paused ? videoPlayer.play() : videoPlayer.pause();
|
||||
break;
|
||||
case 74:
|
||||
// J Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(-10);
|
||||
break;
|
||||
case 75:
|
||||
// K Key
|
||||
event.preventDefault();
|
||||
videoPlayer.paused ? videoPlayer.play() : videoPlayer.pause();
|
||||
break;
|
||||
case 76:
|
||||
// L Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(10);
|
||||
break;
|
||||
case 70:
|
||||
// F Key
|
||||
event.preventDefault();
|
||||
videoPlayer.webkitRequestFullscreen();
|
||||
break;
|
||||
case 77:
|
||||
// M Key
|
||||
event.preventDefault();
|
||||
let volume = videoPlayer.volume;
|
||||
console.log(volume);
|
||||
if (volume > 0){
|
||||
changeVolume(-1);
|
||||
let videoShortcutHandler = function (event) {
|
||||
|
||||
let videoPlayer = $('.videoPlayer').get(0);
|
||||
if (typeof (videoPlayer) !== 'undefined' && !$('#jumpToInput').is(':focus') && !$('#search').is(':focus')) {
|
||||
switch (event.which) {
|
||||
case 32:
|
||||
// Space Bar
|
||||
event.preventDefault();
|
||||
videoPlayer.paused ? videoPlayer.play() : videoPlayer.pause();
|
||||
break;
|
||||
case 74:
|
||||
// J Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(-10);
|
||||
break;
|
||||
case 75:
|
||||
// K Key
|
||||
event.preventDefault();
|
||||
videoPlayer.paused ? videoPlayer.play() : videoPlayer.pause();
|
||||
break;
|
||||
case 76:
|
||||
// L Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(10);
|
||||
break;
|
||||
case 70:
|
||||
// F Key
|
||||
event.preventDefault();
|
||||
videoPlayer.webkitRequestFullscreen();
|
||||
break;
|
||||
case 77:
|
||||
// M Key
|
||||
event.preventDefault();
|
||||
let volume = videoPlayer.volume;
|
||||
|
||||
if (volume > 0) {
|
||||
changeVolume(-1);
|
||||
} else {
|
||||
changeVolume(1);
|
||||
}
|
||||
break;
|
||||
case 67:
|
||||
// C Key
|
||||
let subtitleMode = $('.videoPlayer').get(0).textTracks[0].mode;
|
||||
if (subtitleMode === 'hidden') {
|
||||
$('.videoPlayer').get(0).textTracks[0].mode = 'showing'
|
||||
} else {
|
||||
$('.videoPlayer').get(0).textTracks[0].mode = 'hidden'
|
||||
}
|
||||
break;
|
||||
case 38:
|
||||
// Up Arrow Key
|
||||
event.preventDefault();
|
||||
changeVolume(0.05);
|
||||
break;
|
||||
case 40:
|
||||
// Down Arrow Key
|
||||
event.preventDefault();
|
||||
changeVolume(-0.05);
|
||||
break;
|
||||
case 37:
|
||||
// Left Arrow Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(-5);
|
||||
break;
|
||||
case 39:
|
||||
// Right Arrow Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(5);
|
||||
break;
|
||||
case 49:
|
||||
// 1 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.1);
|
||||
break;
|
||||
case 50:
|
||||
// 2 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.2);
|
||||
break;
|
||||
case 51:
|
||||
// 3 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.3);
|
||||
break;
|
||||
case 52:
|
||||
// 4 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.4);
|
||||
break;
|
||||
case 53:
|
||||
// 5 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.5);
|
||||
break;
|
||||
case 54:
|
||||
// 6 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.6);
|
||||
break;
|
||||
case 55:
|
||||
// 7 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.7);
|
||||
break;
|
||||
case 56:
|
||||
// 8 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.8);
|
||||
break;
|
||||
case 57:
|
||||
// 9 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.9);
|
||||
break;
|
||||
case 48:
|
||||
// 0 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0);
|
||||
break;
|
||||
}
|
||||
else{
|
||||
changeVolume(1);
|
||||
}
|
||||
break;
|
||||
case 67:
|
||||
// C Key
|
||||
let subtitleMode = $('.videoPlayer').get(0).textTracks[0].mode;
|
||||
if (subtitleMode === 'hidden'){
|
||||
$('.videoPlayer').get(0).textTracks[0].mode = 'showing'
|
||||
}
|
||||
else{
|
||||
$('.videoPlayer').get(0).textTracks[0].mode = 'hidden'
|
||||
}
|
||||
break;
|
||||
case 38:
|
||||
// Up Arrow Key
|
||||
event.preventDefault();
|
||||
changeVolume(0.05);
|
||||
break;
|
||||
case 40:
|
||||
// Down Arrow Key
|
||||
event.preventDefault();
|
||||
changeVolume(-0.05);
|
||||
break;
|
||||
case 37:
|
||||
// Left Arrow Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(-5);
|
||||
break;
|
||||
case 39:
|
||||
// Right Arrow Key
|
||||
event.preventDefault();
|
||||
changeDurationBySeconds(5);
|
||||
break;
|
||||
case 49:
|
||||
// 1 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.1);
|
||||
break;
|
||||
case 50:
|
||||
// 2 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.2);
|
||||
break;
|
||||
case 51:
|
||||
// 3 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.3);
|
||||
break;
|
||||
case 52:
|
||||
// 4 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.4);
|
||||
break;
|
||||
case 53:
|
||||
// 5 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.5);
|
||||
break;
|
||||
case 54:
|
||||
// 6 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.6);
|
||||
break;
|
||||
case 55:
|
||||
// 7 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.7);
|
||||
break;
|
||||
case 56:
|
||||
// 8 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.8);
|
||||
break;
|
||||
case 57:
|
||||
// 9 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0.9);
|
||||
break;
|
||||
case 48:
|
||||
// 0 Key
|
||||
event.preventDefault();
|
||||
changeDurationByPercentage(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let fullscreenVideo = function(event){
|
||||
if (document.webkitFullscreenElement !== null){
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
else{
|
||||
$('.videoPlayer').get(0).webkitRequestFullscreen();
|
||||
}
|
||||
let fullscreenVideo = function (event) {
|
||||
if (document.webkitFullscreenElement !== null) {
|
||||
document.webkitExitFullscreen();
|
||||
} else {
|
||||
$('.videoPlayer').get(0).webkitRequestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,6 +204,7 @@ let fullscreenVideo = function(event){
|
|||
* Bind click events
|
||||
* --------------------------
|
||||
*/
|
||||
|
||||
$(document).on('click', '#showComments', showComments);
|
||||
|
||||
$(document).on('click', '.videoPlayer', playPauseVideo);
|
||||
|
@ -221,3 +214,17 @@ $(document).on('dblclick', '.videoPlayer', fullscreenVideo);
|
|||
$(document).on('keydown', videoShortcutHandler);
|
||||
|
||||
$(document).on('click', '#confirmNo', hideConfirmFunction);
|
||||
|
||||
// Open links externally by default
|
||||
$(document).on('click', 'a[href^="http"]', (event) => {
|
||||
let el = event.currentTarget;
|
||||
event.preventDefault();
|
||||
shell.openExternal(el.href);
|
||||
});
|
||||
|
||||
// Open links externally on middle click.
|
||||
$(document).on('auxclick', 'a[href^="http"]', (event) => {
|
||||
let el = event.currentTarget;
|
||||
event.preventDefault();
|
||||
shell.openExternal(el.href);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
let ft = {};
|
||||
|
||||
/**
|
||||
*
|
||||
* Use this function instead of console.log.
|
||||
* This function logs the date, time and presents the information in a readable format
|
||||
*
|
||||
* @param {*} data
|
||||
*
|
||||
* @returns {Void}
|
||||
*/
|
||||
ft.log = function (...data) {
|
||||
let currentTime = new Date();
|
||||
let time = currentTime.getDate() + "/" +
|
||||
(currentTime.getMonth() + 1) + "/" +
|
||||
currentTime.getFullYear() + "@" +
|
||||
currentTime.getHours() + ":" +
|
||||
currentTime.getMinutes() + ":" +
|
||||
currentTime.getSeconds();
|
||||
|
||||
console.log('[' + time + '] ' + '[FREETUBE]', data);
|
||||
}
|
|
@ -1,16 +1,13 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
@ -54,8 +51,8 @@ function removeFromHistory(videoId){
|
|||
* @return {Void}
|
||||
*/
|
||||
function showHistory(){
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
//clearMainContainer();
|
||||
//startLoadingAnimation();
|
||||
console.log('checking history');
|
||||
|
||||
let videoList = '';
|
||||
|
@ -80,15 +77,15 @@ function showHistory(){
|
|||
id: videoList,
|
||||
maxResults: 50,
|
||||
}, function (data) {
|
||||
createVideoListContainer('Watch History:');
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
historyView.videoList = [];
|
||||
loadingView.seen = false;
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video, 'history');
|
||||
});
|
||||
});
|
||||
stopLoadingAnimation()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
219
src/js/init.js
219
src/js/init.js
|
@ -1,28 +1,34 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* File used to initializing the application
|
||||
*/
|
||||
const {app, BrowserWindow, dialog, protocol} = require('electron');
|
||||
* File used to initializing the application
|
||||
*/
|
||||
const {
|
||||
app,
|
||||
BrowserWindow,
|
||||
dialog,
|
||||
protocol
|
||||
} = require('electron');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
|
||||
let win;
|
||||
|
||||
protocol.registerStandardSchemes(['freetube']);
|
||||
|
@ -30,103 +36,144 @@ protocol.registerStandardSchemes(['freetube']);
|
|||
app.setAsDefaultProtocolClient('freetube');
|
||||
|
||||
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (win) {
|
||||
if (win.isMinimized()) win.restore()
|
||||
win.focus()
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (win) {
|
||||
if (win.isMinimized()) win.restore()
|
||||
win.focus()
|
||||
|
||||
win.webContents.send('ping', commandLine)
|
||||
}
|
||||
win.webContents.send('ping', commandLine)
|
||||
}
|
||||
});
|
||||
|
||||
if(require('electron-squirrel-startup') || isSecondInstance) app.quit();
|
||||
if (require('electron-squirrel-startup') || isSecondInstance) app.quit();
|
||||
|
||||
/**
|
||||
* Initialize the Electron application
|
||||
* 1. create the browser window
|
||||
* 2. load the index.html
|
||||
*/
|
||||
let init = function() {
|
||||
const Menu = require('electron').Menu;
|
||||
let init = function () {
|
||||
const Menu = require('electron').Menu;
|
||||
|
||||
win = new BrowserWindow({width: 1200, height: 800, autoHideMenuBar: true});
|
||||
win = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
autoHideMenuBar: true
|
||||
});
|
||||
|
||||
win.loadURL(url.format({
|
||||
pathname: path.join(__dirname, '../index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
}));
|
||||
win.loadURL(url.format({
|
||||
pathname: path.join(__dirname, '../index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
}));
|
||||
|
||||
if (process.env = 'development') {
|
||||
//win.webContents.openDevTools();ff
|
||||
}
|
||||
|
||||
win.on('closed', () => {
|
||||
win = null;
|
||||
});
|
||||
|
||||
const template = [
|
||||
{
|
||||
label: 'File',
|
||||
submenu: [
|
||||
{role: 'quit'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{role: 'cut'},
|
||||
{role: 'copy', accelerator: "CmdOrCtrl+C", selector: "copy:" },
|
||||
{role: 'paste', accelerator: "CmdOrCtrl+V", selector: "paste:" },
|
||||
{role: 'pasteandmatchstyle'},
|
||||
{role: 'delete'},
|
||||
{role: 'selectall'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{role: 'reload'},
|
||||
{role: 'forcereload'},
|
||||
{role: 'toggledevtools'},
|
||||
{type: 'separator'},
|
||||
{role: 'resetzoom'},
|
||||
{role: 'zoomin'},
|
||||
{role: 'zoomout'},
|
||||
{type: 'separator'},
|
||||
{role: 'togglefullscreen'}
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'window',
|
||||
submenu: [
|
||||
{role: 'minimize'},
|
||||
{role: 'close'}
|
||||
]
|
||||
if (process.env = 'development') {
|
||||
//win.webContents.openDevTools();ff
|
||||
}
|
||||
];
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
win.on('closed', () => {
|
||||
win = null;
|
||||
});
|
||||
|
||||
const template = [{
|
||||
label: 'File',
|
||||
submenu: [{
|
||||
label: 'Open New Window',
|
||||
click () { init() }
|
||||
},
|
||||
{role: 'quit'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [{
|
||||
role: 'cut'
|
||||
},
|
||||
{
|
||||
role: 'copy',
|
||||
accelerator: "CmdOrCtrl+C",
|
||||
selector: "copy:"
|
||||
},
|
||||
{
|
||||
role: 'paste',
|
||||
accelerator: "CmdOrCtrl+V",
|
||||
selector: "paste:"
|
||||
},
|
||||
{
|
||||
role: 'pasteandmatchstyle'
|
||||
},
|
||||
{
|
||||
role: 'delete'
|
||||
},
|
||||
{
|
||||
role: 'selectall'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [{
|
||||
role: 'reload'
|
||||
},
|
||||
{
|
||||
role: 'forcereload'
|
||||
},
|
||||
{
|
||||
role: 'toggledevtools'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
role: 'resetzoom'
|
||||
},
|
||||
{
|
||||
role: 'zoomin'
|
||||
},
|
||||
{
|
||||
role: 'zoomout'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
role: 'togglefullscreen'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'window',
|
||||
submenu: [{
|
||||
role: 'minimize'
|
||||
},
|
||||
{
|
||||
role: 'close'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
};
|
||||
|
||||
/**
|
||||
* Quit the application
|
||||
*/
|
||||
let allWindowsClosed = function() {
|
||||
win.webContents.session.clearStorageData([], (data) => {});
|
||||
win.webContents.session.clearCache((data) => {});
|
||||
app.quit();
|
||||
let allWindowsClosed = function () {
|
||||
win.webContents.session.clearStorageData([], (data) => {});
|
||||
win.webContents.session.clearCache((data) => {});
|
||||
app.quit();
|
||||
};
|
||||
|
||||
/**
|
||||
* On Mac, when dock icon is clicked,
|
||||
* create a new window and launch the editor
|
||||
*/
|
||||
let active = function() {
|
||||
if (win === null) {
|
||||
init();
|
||||
}
|
||||
let active = function () {
|
||||
if (win === null) {
|
||||
init();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
272
src/js/layout.js
272
src/js/layout.js
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If nsot, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -23,103 +23,60 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
||||
|
||||
// Add general variables. Please put all require statements here.
|
||||
const Datastore = require('nedb'); // database logic
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
const mustache = require('mustache'); // templating
|
||||
const dateFormat = require('dateformat'); // formatting dates
|
||||
|
||||
//const RxPlayer = require('rx-player'); // formatting dates
|
||||
|
||||
// Used for finding links within text and making them clickable. Used mostly for video descriptions.
|
||||
const autolinker = require('autolinker');
|
||||
const electron = require('electron');
|
||||
const protocol = electron.remote.protocol;
|
||||
|
||||
// Used for getting the user's subscriptions. Can probably remove this when that function
|
||||
// is rewritten.
|
||||
//const asyncLoop = require('node-async-loop');
|
||||
//const youtubedl = require('youtube-dl');
|
||||
const ytdl = require('ytdl-core');
|
||||
const shell = electron.shell; // Used to open external links into the user's native browser.
|
||||
const localDataStorage = electron.remote.app.getPath('userData'); // Grabs the userdata directory based on the user's OS
|
||||
const clipboard = electron.clipboard;
|
||||
const getOpml = require('opml-to-json'); // Gets the file type for imported files.
|
||||
const fs = require('fs'); // Used to read files. Specifically in the settings page.
|
||||
const tor = require('tor-request');
|
||||
|
||||
let currentTheme = '';
|
||||
let apiKey;
|
||||
let useTor = false;
|
||||
let dialog = electron.remote.dialog; // Used for opening file browser to export / import subscriptions.
|
||||
let toastTimeout; // Timeout for toast notifications.
|
||||
let mouseTimeout; // Timeout for hiding the mouse cursor on video playback
|
||||
|
||||
require.extensions['.html'] = function(module, filename) {
|
||||
module.exports = fs.readFileSync(filename, 'utf8');
|
||||
require.extensions['.html'] = function (module, filename) {
|
||||
module.exports = fs.readFileSync(filename, 'utf8');
|
||||
};
|
||||
|
||||
const subDb = new Datastore({
|
||||
filename: localDataStorage + '/subscriptions.db',
|
||||
autoload: true
|
||||
});
|
||||
|
||||
const historyDb = new Datastore({
|
||||
filename: localDataStorage + '/videohistory.db',
|
||||
autoload: true
|
||||
});
|
||||
|
||||
const savedVidsDb = new Datastore({
|
||||
filename: localDataStorage + '/savedvideos.db',
|
||||
autoload: true
|
||||
});
|
||||
|
||||
const settingsDb = new Datastore({
|
||||
filename: localDataStorage + '/settings.db',
|
||||
autoload: true
|
||||
});
|
||||
|
||||
// Grabs the default settings from the settings database file. Makes defaults if
|
||||
// none are found.
|
||||
checkDefaultSettings();
|
||||
|
||||
require('electron').ipcRenderer.on('ping', function(event, message) {
|
||||
console.log(message);
|
||||
electron.ipcRenderer.on('ping', function(event, message) {
|
||||
ft.log(message);
|
||||
let url = message[1].replace('freetube://', '');
|
||||
parseSearchText(url);
|
||||
console.log(message);
|
||||
ft.log(message);
|
||||
});
|
||||
|
||||
// Open links externally by default
|
||||
$(document).on('click', 'a[href^="http"]', (event) => {
|
||||
let el = event.currentTarget;
|
||||
event.preventDefault();
|
||||
shell.openExternal(el.href);
|
||||
});
|
||||
|
||||
// Open links externally on middle click.
|
||||
$(document).on('auxclick', 'a[href^="http"]', (event) => {
|
||||
let el = event.currentTarget;
|
||||
event.preventDefault();
|
||||
shell.openExternal(el.href);
|
||||
});
|
||||
|
||||
|
||||
$(document).ready(() => {
|
||||
const searchBar = document.getElementById('search');
|
||||
const jumpToInput = document.getElementById('jumpToInput');
|
||||
const searchBar = document.getElementById('search');
|
||||
const jumpToInput = document.getElementById('jumpToInput');
|
||||
|
||||
// Displays the list of subscriptions in the side bar.
|
||||
displaySubs();
|
||||
// Displays the list of subscriptions in the side bar.
|
||||
displaySubs();
|
||||
|
||||
// Allow user to use the 'enter' key to search for a video.
|
||||
searchBar.onkeypress = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
parseSearchText();
|
||||
}
|
||||
};
|
||||
// Allow user to use the 'enter' key to search for a video.
|
||||
searchBar.onkeypress = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
parseSearchText();
|
||||
}
|
||||
};
|
||||
|
||||
// Display subscriptions upon the app opening up. May allow user to specify.
|
||||
// Home page in the future.
|
||||
loadingView.seen = true;
|
||||
loadSubscriptions();
|
||||
});
|
||||
|
||||
|
@ -129,94 +86,16 @@ $(document).ready(() => {
|
|||
* @return {Void}
|
||||
*/
|
||||
function toggleSideNavigation() {
|
||||
const sideNav = document.getElementById('sideNav');
|
||||
const mainContainer = document.getElementById('main');
|
||||
const sideNav = document.getElementById('sideNav');
|
||||
const mainContainer = document.getElementById('main');
|
||||
|
||||
if (sideNav.style.display === 'none') {
|
||||
sideNav.style.display = 'inline';
|
||||
mainContainer.style.marginLeft = '250px';
|
||||
} else {
|
||||
sideNav.style.display = 'none';
|
||||
mainContainer.style.marginLeft = '0px';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the #main container to allow other information to be shown.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function clearMainContainer() {
|
||||
const container = document.getElementById('main');
|
||||
container.innerHTML = '';
|
||||
hideConfirmFunction();
|
||||
}
|
||||
|
||||
function startLoadingAnimation() {
|
||||
const loading = document.getElementById('loading');
|
||||
const sideNavDisabled = document.getElementById('sideNavDisabled');
|
||||
const searchBar = document.getElementById('search');
|
||||
|
||||
loading.style.display = 'inherit';
|
||||
if(sideNavDisabled !== null){
|
||||
sideNavDisabled.style.display = 'inherit';
|
||||
}
|
||||
|
||||
searchBar.disabled = true;
|
||||
}
|
||||
|
||||
function stopLoadingAnimation() {
|
||||
const loading = document.getElementById('loading');
|
||||
const sideNavDisabled = document.getElementById('sideNavDisabled');
|
||||
const searchBar = document.getElementById('search');
|
||||
|
||||
loading.style.display = 'none';
|
||||
if(sideNavDisabled !== null){
|
||||
sideNavDisabled.style.display = 'none';
|
||||
}
|
||||
|
||||
searchBar.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a div container in #main meant to be a container for video lists.
|
||||
*
|
||||
* @param {string} headerLabel - The header of the container. Not used for showing video recommendations.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function createVideoListContainer(headerLabel = '') {
|
||||
const videoListContainer = document.createElement("div");
|
||||
videoListContainer.id = 'videoListContainer';
|
||||
let headerSpacer;
|
||||
if (headerLabel != '') {
|
||||
const headerElement = document.createElement("h2");
|
||||
headerElement.innerHTML = headerLabel;
|
||||
headerElement.style.marginLeft = '15px';
|
||||
headerElement.appendChild(document.createElement("hr"));
|
||||
videoListContainer.appendChild(headerElement);
|
||||
}
|
||||
document.getElementById("main").appendChild(videoListContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the about page to #main
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function showAbout() {
|
||||
// Remove current information and display loading animation
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
|
||||
const aboutTemplate = require('./templates/about.html')
|
||||
mustache.parse(aboutTemplate);
|
||||
$('#main').html(
|
||||
mustache.render(aboutTemplate, {
|
||||
versionNumber: require('electron').remote.app.getVersion(),
|
||||
})
|
||||
);
|
||||
stopLoadingAnimation();
|
||||
if (sideNav.style.display === 'none') {
|
||||
sideNav.style.display = 'inline';
|
||||
mainContainer.style.marginLeft = '250px';
|
||||
} else {
|
||||
sideNav.style.display = 'none';
|
||||
mainContainer.style.marginLeft = '0px';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,18 +107,18 @@ function showAbout() {
|
|||
* @return {Void}
|
||||
*/
|
||||
function showToast(message) {
|
||||
let toast = document.getElementById('toast');
|
||||
let toastMessage = document.getElementById('toastMessage');
|
||||
let toast = document.getElementById('toast');
|
||||
let toastMessage = document.getElementById('toastMessage');
|
||||
|
||||
// If a toast message is already being displayed, this will remove the previous timer that was set.
|
||||
clearTimeout(toastTimeout);
|
||||
// If a toast message is already being displayed, this will remove the previous timer that was set.
|
||||
clearTimeout(toastTimeout);
|
||||
|
||||
toastMessage.innerHTML = message;
|
||||
toast.style.visibility = 'visible';
|
||||
toast.style.opacity = 0.9;
|
||||
toastMessage.innerHTML = message;
|
||||
toast.style.visibility = 'visible';
|
||||
toast.style.opacity = 0.9;
|
||||
|
||||
// Set the timer for the toast to be removed.
|
||||
toastTimeout = window.setTimeout(hideToast, 5000);
|
||||
// Set the timer for the toast to be removed.
|
||||
toastTimeout = window.setTimeout(hideToast, 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,9 +127,9 @@ function showToast(message) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function hideToast() {
|
||||
let toast = document.getElementById('toast');
|
||||
toast.style.opacity = 0;
|
||||
toast.style.visibility = 'hidden';
|
||||
let toast = document.getElementById('toast');
|
||||
toast.style.opacity = 0;
|
||||
toast.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -264,21 +143,20 @@ function hideToast() {
|
|||
* @return {Void}
|
||||
*/
|
||||
function confirmFunction(message, performFunction, parameters = '') {
|
||||
let confirmContainer = document.getElementById('confirmFunction');
|
||||
let confirmMessage = document.getElementById('confirmMessage');
|
||||
let confirmContainer = document.getElementById('confirmFunction');
|
||||
let confirmMessage = document.getElementById('confirmMessage');
|
||||
|
||||
confirmMessage.innerHTML = message;
|
||||
confirmContainer.style.visibility = 'visible';
|
||||
confirmMessage.innerHTML = message;
|
||||
confirmContainer.style.visibility = 'visible';
|
||||
|
||||
$(document).on('click', '#confirmYes', (event) => {
|
||||
if(parameters != ''){
|
||||
performFunction(parameters);
|
||||
}
|
||||
else{
|
||||
performFunction();
|
||||
}
|
||||
hideConfirmFunction();
|
||||
});
|
||||
$(document).on('click', '#confirmYes', (event) => {
|
||||
if (parameters != '') {
|
||||
performFunction(parameters);
|
||||
} else {
|
||||
performFunction();
|
||||
}
|
||||
hideConfirmFunction();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -287,8 +165,8 @@ function confirmFunction(message, performFunction, parameters = '') {
|
|||
* @return {Void}
|
||||
*/
|
||||
function hideConfirmFunction() {
|
||||
let confirmContainer = document.getElementById('confirmFunction');
|
||||
confirmContainer.style.visibility = 'hidden';
|
||||
let confirmContainer = document.getElementById('confirmFunction');
|
||||
confirmContainer.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,11 +176,11 @@ function hideConfirmFunction() {
|
|||
* @return {Void}
|
||||
*/
|
||||
function hideMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
mouseTimeout = window.setTimeout(function() {
|
||||
$('.videoPlayer')[0].style.cursor = 'none';
|
||||
}, 3150);
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
mouseTimeout = window.setTimeout(function () {
|
||||
$('.videoPlayer')[0].style.cursor = 'none';
|
||||
}, 3150);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,14 +189,14 @@ function hideMouseTimeout() {
|
|||
* @return {Void}
|
||||
*/
|
||||
function removeMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
}
|
||||
|
||||
function showVideoOptions(element) {
|
||||
if (element.nextElementSibling.style.display == 'none' || element.nextElementSibling.style.display == '') {
|
||||
element.nextElementSibling.style.display = 'inline-block'
|
||||
} else {
|
||||
element.nextElementSibling.style.display = 'none'
|
||||
}
|
||||
}
|
||||
if (element.nextElementSibling.style.display == 'none' || element.nextElementSibling.style.display == '') {
|
||||
element.nextElementSibling.style.display = 'inline-block'
|
||||
} else {
|
||||
element.nextElementSibling.style.display = 'none'
|
||||
}
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -21,9 +21,9 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
* Video Comment Model
|
||||
*/
|
||||
export class comment {
|
||||
authorDisplayName: string;
|
||||
authorProfileImageUrl: string;
|
||||
authorChannelId: string;
|
||||
textDisplay: string;
|
||||
publishedAt: string;
|
||||
}
|
||||
authorDisplayName;
|
||||
authorProfileImageUrl;
|
||||
authorChannelId;
|
||||
textDisplay;
|
||||
publishedAt;
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
|
403
src/js/player.js
403
src/js/player.js
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -28,128 +28,101 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
|
||||
if (useWindowPlayer === false){
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
}
|
||||
else{
|
||||
showToast('Getting video information. Please wait...')
|
||||
}
|
||||
function playVideo(videoId) {
|
||||
hideViews();
|
||||
|
||||
let subscribeText = '';
|
||||
let savedText = '';
|
||||
let savedIconClass = '';
|
||||
let savedIconColor = '';
|
||||
let video480p;
|
||||
let video720p;
|
||||
let videoSubtitles = '';
|
||||
let subtitleHtml = '';
|
||||
let subtitleLabel;
|
||||
let subtitleLanguage;
|
||||
let subtitleCode;
|
||||
let subtitleUrl;
|
||||
let defaultUrl;
|
||||
let defaultQuality;
|
||||
let channelId;
|
||||
let videoHtml;
|
||||
let videoType = 'video';
|
||||
let embedPlayer = "<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/" + videoId + "?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>";
|
||||
let useEmbedPlayer = false;
|
||||
let validUrl;
|
||||
let videoLikes;
|
||||
let videoDislikes;
|
||||
let totalLikes;
|
||||
let likePercentage;
|
||||
playerView.playerSeen = true;
|
||||
playerView.videoId = videoId;
|
||||
playerView.video480p = undefined;
|
||||
playerView.video720p = undefined;
|
||||
playerView.embededHtml = "<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/" + videoId + "?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>";
|
||||
|
||||
const checkSavedVideo = videoIsSaved(videoId);
|
||||
const checkSavedVideo = videoIsSaved(videoId);
|
||||
|
||||
// Change the save button icon and text depending on if the user has saved the video or not.
|
||||
checkSavedVideo.then((results) => {
|
||||
if (results === false) {
|
||||
savedText = 'SAVE';
|
||||
savedIconClass = 'far unsaved';
|
||||
playerView.savedText = 'FAVORITE';
|
||||
playerView.savedIconType = 'far unsaved';
|
||||
} else {
|
||||
savedText = 'SAVED';
|
||||
savedIconClass = 'fas saved';
|
||||
playerView.savedText = 'FAVORITED';
|
||||
playerView.savedIconType = 'fas saved';
|
||||
}
|
||||
});
|
||||
|
||||
youtubeAPI('videos', {
|
||||
part: 'statistics',
|
||||
id: videoId,
|
||||
}, function(data) {
|
||||
console.log(data);
|
||||
youtubeAPI('videos', {
|
||||
part: 'statistics',
|
||||
id: videoId,
|
||||
}, function (data) {
|
||||
|
||||
// Figure out the width for the like/dislike bar.
|
||||
videoLikes = data['items'][0]['statistics']['likeCount'];
|
||||
videoDislikes = data['items'][0]['statistics']['dislikeCount'];
|
||||
totalLikes = parseInt(videoLikes) + parseInt(videoDislikes);
|
||||
likePercentage = parseInt((videoLikes / totalLikes) * 100);
|
||||
playerView.videoLikes = data['items'][0]['statistics']['likeCount'];
|
||||
playerView.videoDislikes = data['items'][0]['statistics']['dislikeCount'];
|
||||
let totalLikes = parseInt(playerView.videoLikes) + parseInt(playerView.videoDislikes);
|
||||
playerView.likePercentage = parseInt((playerView.videoLikes / totalLikes) * 100);
|
||||
});
|
||||
|
||||
/*
|
||||
* FreeTube calls youtube-dl to grab the direct video URL.
|
||||
*/
|
||||
youtubedlGetInfo(videoId, (info) => {
|
||||
console.log(info);
|
||||
/*
|
||||
* FreeTube calls youtube-dl to grab the direct video URL.
|
||||
*/
|
||||
youtubedlGetInfo(videoId, (info) => {
|
||||
|
||||
console.log(videoLikes);
|
||||
|
||||
channelId = info['author']['id'];
|
||||
let channelThumbnail = info['author']['avatar'];
|
||||
playerView.videoTitle = info['title'];
|
||||
playerView.channelName = info['author']['name'];
|
||||
playerView.channelId = info['author']['id'];
|
||||
playerView.channelIcon = info['author']['avatar'];
|
||||
|
||||
let videoUrls = info['formats'];
|
||||
|
||||
// Add commas to the video view count.
|
||||
const videoViews = info['view_count'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
playerView.videoViews = info['view_count'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
|
||||
videoThumbnail = info['player_response']['videoDetails']['thumbnail']['thumbnails'][3]['url'];
|
||||
playerView.videoThumbnail = info['player_response']['videoDetails']['thumbnail']['thumbnails'][3]['url'];
|
||||
|
||||
// Format the date to a more readable format.
|
||||
let dateString = new Date(info['published']);
|
||||
dateString.setDate(dateString.getDate() + 1);
|
||||
const publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
playerView.publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
|
||||
let description = info['description'];
|
||||
// Adds clickable links to the description.
|
||||
description = autolinker.link(description);
|
||||
playerView.description = autolinker.link(description);
|
||||
|
||||
// Search through the returned object to get the 480p and 720p video URLs (If available)
|
||||
Object.keys(videoUrls).forEach((key) => {
|
||||
switch (videoUrls[key]['itag']) {
|
||||
case '18':
|
||||
video480p = decodeURIComponent(videoUrls[key]['url']);
|
||||
console.log(video480p);
|
||||
playerView.video480p = decodeURIComponent(videoUrls[key]['url']);
|
||||
//console.log(video480p);
|
||||
break;
|
||||
case '22':
|
||||
video720p = decodeURIComponent(videoUrls[key]['url']);
|
||||
console.log(video720p);
|
||||
playerView.video720p = decodeURIComponent(videoUrls[key]['url']);
|
||||
//console.log(video720p);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
let useEmbedPlayer = false;
|
||||
|
||||
// Default to the embeded player if the URLs cannot be found.
|
||||
if (typeof(video720p) === 'undefined' && typeof(video480p) === 'undefined') {
|
||||
if (typeof(playerView.video720p) === 'undefined' && typeof(playerView.video480p) === 'undefined') {
|
||||
//useEmbedPlayer = true;
|
||||
playerView.currentQuality = 'EMBED';
|
||||
playerView.playerSeen = false;
|
||||
useEmbedPlayer = true;
|
||||
defaultQuality = 'EMBED';
|
||||
videoHtml = embedPlayer.replace(/\"\;/g, '"');
|
||||
showToast('Unable to get video file. Reverting to embeded player.');
|
||||
} else if (typeof(video720p) === 'undefined' && typeof(video480p) !== 'undefined') {
|
||||
// Default to the 480p video if the 720p URL cannot be found.
|
||||
defaultUrl = video480p;
|
||||
defaultQuality = '480p';
|
||||
playerView.videoUrl = playerView.video480p;
|
||||
playerView.currentQuality = '480p';
|
||||
} else {
|
||||
// Default to the 720p video.
|
||||
defaultUrl = video720p;
|
||||
defaultQuality = '720p';
|
||||
// Force the embeded player if needed.
|
||||
//videoHtml = embedPlayer;
|
||||
playerView.videoUrl = playerView.video720p;
|
||||
playerView.currentQuality = '720p';
|
||||
}
|
||||
|
||||
if (!useEmbedPlayer) {
|
||||
//videoHtml = '<video class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src="' + defaultUrl + '" poster="' + videoThumbnail + '" autoplay>';
|
||||
|
||||
let videoHtml = '';
|
||||
|
||||
if (typeof(info.player_response.captions) === 'object') {
|
||||
if (typeof(info.player_response.captions.playerCaptionsTracklistRenderer.captionTracks) === 'object') {
|
||||
|
@ -168,100 +141,46 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
|
|||
}
|
||||
}
|
||||
|
||||
//videoHtml = videoHtml + '</video>';
|
||||
}
|
||||
playerView.subtitleHtml = videoHtml;
|
||||
}
|
||||
|
||||
const checkSubscription = isSubscribed(channelId);
|
||||
|
||||
// Change the subscribe button text depending on if the user has subscribed to the channel or not.
|
||||
const checkSubscription = isSubscribed(playerView.channelId);
|
||||
|
||||
checkSubscription.then((results) => {
|
||||
const subscribeButton = document.getElementById('subscribeButton');
|
||||
|
||||
if (results === false) {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'SUBSCRIBE';
|
||||
playerView.subscribedText = 'SUBSCRIBE';
|
||||
}
|
||||
} else {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'UNSUBSCRIBE';
|
||||
playerView.subscribedText = 'UNSUBSCRIBE';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const playerTemplate = require('./templates/player.html')
|
||||
mustache.parse(playerTemplate);
|
||||
const rendered = mustache.render(playerTemplate, {
|
||||
videoQuality: defaultQuality,
|
||||
subtitleHtml: videoHtml,
|
||||
defaultUrl: defaultUrl,
|
||||
videoTitle: info['title'],
|
||||
videoViews: videoViews,
|
||||
videoThumbnail: videoThumbnail,
|
||||
channelName: info['author']['name'],
|
||||
videoLikes: videoLikes,
|
||||
videoDislikes: videoDislikes,
|
||||
likePercentage: likePercentage,
|
||||
videoId: videoId,
|
||||
channelId: channelId,
|
||||
channelIcon: channelThumbnail,
|
||||
publishedDate: publishedDate,
|
||||
description: description,
|
||||
isSubscribed: subscribeText,
|
||||
savedText: savedText,
|
||||
savedIconClass: savedIconClass,
|
||||
savedIconColor: savedIconColor,
|
||||
video480p: video480p,
|
||||
video720p: video720p,
|
||||
embedPlayer: embedPlayer,
|
||||
});
|
||||
showVideoRecommendations(videoId);
|
||||
|
||||
// Add the video to the user's history
|
||||
addToHistory(videoId);
|
||||
loadingView.seen = false;
|
||||
|
||||
if (useWindowPlayer){
|
||||
// Create a new browser window.
|
||||
const BrowserWindow = electron.remote.BrowserWindow;
|
||||
|
||||
let newWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 700
|
||||
});
|
||||
|
||||
let playerWindowHeader = require('./templates/playerWindow.html');
|
||||
|
||||
mustache.parse(playerWindowHeader);
|
||||
const playerHeaderRender = mustache.render(playerWindowHeader, {
|
||||
videoId: videoId,
|
||||
channelId: channelId
|
||||
});
|
||||
|
||||
newWindow.loadURL('data:text/html;charset=UTF-8,' + encodeURIComponent(playerHeaderRender + rendered), {
|
||||
baseURLForDataURL: `file://${__dirname}/src`
|
||||
});
|
||||
if (subscriptionView.seen === false && aboutView.seen === false && headerView.seen === false && searchView.seen === false && settingsView.seen === false && popularView.seen === false && savedView.seen === false && historyView.seen === false && channelView.seen === false && channelVideosView.seen === false) {
|
||||
playerView.seen = true;
|
||||
}
|
||||
else{
|
||||
$('#main').html(rendered);
|
||||
stopLoadingAnimation();
|
||||
|
||||
showVideoRecommendations(videoId);
|
||||
|
||||
// Hide subtitles by default
|
||||
if (typeof(info['subtitles']) !== 'undefined' && Object.keys(info['subtitles']).length > 0) {
|
||||
let textTracks = $('.videoPlayer').get(0).textTracks;
|
||||
Object.keys(textTracks).forEach((track) => {
|
||||
textTracks[track].mode = 'hidden';
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes a video URL is found, but the video will not play. I believe the issue is
|
||||
// that the video has yet to render for that quality, as the video will be available at a later time.
|
||||
// This will check the URLs and switch video sources if there is an error.
|
||||
//checkVideoUrls(video480p, video720p);
|
||||
addToHistory(videoId);
|
||||
|
||||
window.setTimeout(checkVideoUrls, 5000, video480p, video720p);
|
||||
// Hide subtitles by default
|
||||
if (typeof(info['subtitles']) !== 'undefined' && Object.keys(info['subtitles']).length > 0) {
|
||||
let textTracks = $('.videoPlayer').get(0).textTracks;
|
||||
Object.keys(textTracks).forEach((track) => {
|
||||
textTracks[track].mode = 'hidden';
|
||||
});
|
||||
}
|
||||
|
||||
window.setTimeout(checkVideoUrls, 5000, playerView.video480p, playerView.video720p);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -272,40 +191,40 @@ function playVideo(videoId, videoThumbnail = '', useWindowPlayer = false) {
|
|||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function openMiniPlayer(videoThumbnail) {
|
||||
let lastTime;
|
||||
let videoHtml;
|
||||
function openMiniPlayer() {
|
||||
let lastTime;
|
||||
let videoHtml;
|
||||
|
||||
// Grabs whatever the HTML is for the current video player. Done this way to grab
|
||||
// the HTML5 player (with varying qualities) as well as the YouTube embeded player.
|
||||
if ($('.videoPlayer').length > 0) {
|
||||
$('.videoPlayer').get(0).pause();
|
||||
lastTime = $('.videoPlayer').get(0).currentTime;
|
||||
videoHtml = $('.videoPlayer').get(0).outerHTML;
|
||||
} else {
|
||||
videoHtml = $('iframe').get(0).outerHTML;
|
||||
}
|
||||
// Grabs whatever the HTML is for the current video player. Done this way to grab
|
||||
// the HTML5 player (with varying qualities) as well as the YouTube embeded player.
|
||||
if ($('.videoPlayer').length > 0) {
|
||||
$('.videoPlayer').get(0).pause();
|
||||
lastTime = $('.videoPlayer').get(0).currentTime;
|
||||
videoHtml = $('.videoPlayer').get(0).outerHTML;
|
||||
} else {
|
||||
videoHtml = $('iframe').get(0).outerHTML;
|
||||
}
|
||||
|
||||
// Create a new browser window.
|
||||
const BrowserWindow = electron.remote.BrowserWindow;
|
||||
// Create a new browser window.
|
||||
const BrowserWindow = electron.remote.BrowserWindow;
|
||||
|
||||
let miniPlayer = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 710
|
||||
});
|
||||
let miniPlayer = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 710
|
||||
});
|
||||
|
||||
// Use the miniPlayer.html template.
|
||||
$.get('templates/miniPlayer.html', (template) => {
|
||||
mustache.parse(template);
|
||||
const rendered = mustache.render(template, {
|
||||
videoHtml: videoHtml,
|
||||
videoThumbnail: videoThumbnail,
|
||||
startTime: lastTime,
|
||||
});
|
||||
// Render the template to the new browser window.
|
||||
miniPlayer.loadURL("data:text/html;charset=utf-8," + encodeURI(rendered));
|
||||
});
|
||||
}
|
||||
// Use the miniPlayer.html template.
|
||||
$.get('templates/miniPlayer.html', (template) => {
|
||||
mustache.parse(template);
|
||||
const rendered = mustache.render(template, {
|
||||
videoHtml: videoHtml,
|
||||
videoThumbnail: playerView.thumbnail,
|
||||
startTime: lastTime,
|
||||
});
|
||||
// Render the template to the new browser window.
|
||||
miniPlayer.loadURL("data:text/html;charset=utf-8," + encodeURI(rendered));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the quality of the current video.
|
||||
|
@ -316,54 +235,54 @@ function openMiniPlayer(videoThumbnail) {
|
|||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function changeQuality(videoHtml, qualityType, isEmbed = false) {
|
||||
function changeQuality(url, qualityText, isEmbed = false) {
|
||||
if (videoHtml == '') {
|
||||
showToast('Video quality type is not available. Unable to change quality.')
|
||||
return;
|
||||
}
|
||||
|
||||
videoHtml = videoHtml.replace(/\"\;/g, '"');
|
||||
videoHtml = videoHtml.replace(/\"\;/g, '"');
|
||||
|
||||
console.log(videoHtml);
|
||||
console.log(isEmbed);
|
||||
ft.log('HTML Video: ', videoHtml);
|
||||
ft.log('(Is the video embeded?) isEmbed: ', isEmbed);
|
||||
|
||||
// The YouTube API creates 2 more iFrames. This is why a boolean value is sent
|
||||
// with the function.
|
||||
const embedPlayer = document.getElementsByTagName('IFRAME')[0];
|
||||
// The YouTube API creates 2 more iFrames. This is why a boolean value is sent
|
||||
// with the function.
|
||||
const embedPlayer = document.getElementsByTagName('IFRAME')[0];
|
||||
|
||||
const html5Player = document.getElementsByClassName('videoPlayer');
|
||||
const html5Player = document.getElementsByClassName('videoPlayer');
|
||||
|
||||
console.log(embedPlayer);
|
||||
console.log(html5Player);
|
||||
ft.log('Embeded Player Element: ', embedPlayer);
|
||||
ft.log('HTML5 Player Element: ', html5Player);
|
||||
|
||||
if (isEmbed && html5Player.length == 0) {
|
||||
// The embeded player is already playing. Return.
|
||||
showToast('You are already using the embeded player.')
|
||||
return;
|
||||
} else if (isEmbed) {
|
||||
// Switch from HTML 5 player to embeded Player
|
||||
html5Player[0].remove();
|
||||
const mainHtml = $('#main').html();
|
||||
$('#main').html(videoHtml + mainHtml);
|
||||
$('#currentQuality').html(qualityType);
|
||||
} else if (html5Player.length == 0) {
|
||||
// Switch from embeded player to HTML 5 player
|
||||
embedPlayer.remove();
|
||||
let videoPlayer = document.createElement('video');
|
||||
videoPlayer.className = 'videoPlayer';
|
||||
videoPlayer.src = videoHtml;
|
||||
videoPlayer.controls = true;
|
||||
videoPlayer.autoplay = true;
|
||||
$('#main').prepend(videoPlayer);
|
||||
$('#currentQuality').html(qualityType);
|
||||
} else {
|
||||
// Switch src on HTML 5 player
|
||||
const currentPlayBackTime = $('.videoPlayer').get(0).currentTime;
|
||||
html5Player[0].src = videoHtml;
|
||||
html5Player[0].load();
|
||||
$('.videoPlayer').get(0).currentTime = currentPlayBackTime;
|
||||
$('#currentQuality').html(qualityType);
|
||||
}
|
||||
if (isEmbed && html5Player.length == 0) {
|
||||
// The embeded player is already playing. Return.
|
||||
showToast('You are already using the embeded player.')
|
||||
return;
|
||||
} else if (isEmbed) {
|
||||
// Switch from HTML 5 player to embeded Player
|
||||
html5Player[0].remove();
|
||||
const mainHtml = $('#main').html();
|
||||
$('#main').html(videoHtml + mainHtml);
|
||||
$('#currentQuality').html(qualityType);
|
||||
} else if (html5Player.length == 0) {
|
||||
// Switch from embeded player to HTML 5 player
|
||||
embedPlayer.remove();
|
||||
let videoPlayer = document.createElement('video');
|
||||
videoPlayer.className = 'videoPlayer';
|
||||
videoPlayer.src = videoHtml;
|
||||
videoPlayer.controls = true;
|
||||
videoPlayer.autoplay = true;
|
||||
$('#main').prepend(videoPlayer);
|
||||
$('#currentQuality').html(qualityType);
|
||||
} else {
|
||||
// Switch src on HTML 5 player
|
||||
const currentPlayBackTime = $('.videoPlayer').get(0).currentTime;
|
||||
html5Player[0].src = videoHtml;
|
||||
html5Player[0].load();
|
||||
$('.videoPlayer').get(0).currentTime = currentPlayBackTime;
|
||||
$('#currentQuality').html(qualityType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -374,8 +293,8 @@ function changeQuality(videoHtml, qualityType, isEmbed = false) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function changeVideoSpeed(speed) {
|
||||
$('#currentSpeed').html(speed);
|
||||
$('.videoPlayer').get(0).playbackRate = speed;
|
||||
$('#currentSpeed').html(speed);
|
||||
$('.videoPlayer').get(0).playbackRate = speed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -386,16 +305,16 @@ function changeVideoSpeed(speed) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function changeVolume(amount) {
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
let volume = videoPlayer.volume;
|
||||
volume = volume + amount;
|
||||
if (volume > 1) {
|
||||
videoPlayer.volume = 1;
|
||||
} else if (volume < 0) {
|
||||
videoPlayer.volume = 0;
|
||||
} else {
|
||||
videoPlayer.volume = volume;
|
||||
}
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
let volume = videoPlayer.volume;
|
||||
volume = volume + amount;
|
||||
if (volume > 1) {
|
||||
videoPlayer.volume = 1;
|
||||
} else if (volume < 0) {
|
||||
videoPlayer.volume = 0;
|
||||
} else {
|
||||
videoPlayer.volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -406,8 +325,8 @@ function changeVolume(amount) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function changeDurationBySeconds(seconds) {
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
videoPlayer.currentTime = videoPlayer.currentTime + seconds;
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
videoPlayer.currentTime = videoPlayer.currentTime + seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -418,6 +337,6 @@ function changeDurationBySeconds(seconds) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function changeDurationByPercentage(percentage) {
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
videoPlayer.currentTime = videoPlayer.duration * percentage;
|
||||
const videoPlayer = $('.videoPlayer').get(0);
|
||||
videoPlayer.currentTime = videoPlayer.duration * percentage;
|
||||
}
|
||||
|
|
|
@ -1,118 +1,111 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* File used for functions related to saving videos
|
||||
*/
|
||||
* File used for functions related to saving videos
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a video to the user's saved video database.
|
||||
*
|
||||
* @param {string} videoId - The video ID of the video that will be saved.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function addSavedVideo(videoId){
|
||||
let checkIfSaved = videoIsSaved(videoId);
|
||||
* Adds a video to the user's saved video database.
|
||||
*
|
||||
* @param {string} videoId - The video ID of the video that will be saved.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function addSavedVideo(videoId) {
|
||||
let checkIfSaved = videoIsSaved(videoId);
|
||||
|
||||
checkIfSaved.then((saved) => {
|
||||
if (saved === false){
|
||||
let data = {
|
||||
videoId: videoId,
|
||||
timeSaved: new Date().getTime(),
|
||||
};
|
||||
checkIfSaved.then((saved) => {
|
||||
if (saved === false) {
|
||||
let data = {
|
||||
videoId: videoId,
|
||||
timeSaved: new Date().getTime(),
|
||||
};
|
||||
|
||||
savedVidsDb.insert(data, (err, newDoc) => {
|
||||
showToast('Video has been saved!');
|
||||
});
|
||||
}
|
||||
else{
|
||||
showToast('Video already exists in saved file.')
|
||||
}
|
||||
});
|
||||
savedVidsDb.insert(data, (err, newDoc) => {
|
||||
showToast('The video has been favorited!');
|
||||
});
|
||||
} else {
|
||||
showToast('The video has already been favorited!')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a video from the user's saved video database.
|
||||
*
|
||||
* @param {string} videoId - The video ID of the video that will be removed.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function removeSavedVideo(videoId, string){
|
||||
savedVidsDb.remove({
|
||||
videoId: videoId
|
||||
}, {}, (err, numRemoved) => {
|
||||
showToast('Video has been removed from saved list.');
|
||||
});
|
||||
* Removes a video from the user's saved video database.
|
||||
*
|
||||
* @param {string} videoId - The video ID of the video that will be removed.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function removeSavedVideo(videoId, string) {
|
||||
savedVidsDb.remove({
|
||||
videoId: videoId
|
||||
}, {}, (err, numRemoved) => {
|
||||
showToast('Video has been removed from the favorites list.');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the save video button styling and saved / remove a video based on the current status.
|
||||
*
|
||||
* @param {string} videoId - The video ID to toggle between.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
* Toggles the save video button styling and saved / remove a video based on the current status.
|
||||
*
|
||||
* @param {string} videoId - The video ID to toggle between.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function toggleSavedVideo(videoId) {
|
||||
event.stopPropagation();
|
||||
event.stopPropagation();
|
||||
|
||||
const checkIfSaved = videoIsSaved(videoId);
|
||||
const saveIcon = document.getElementById('saveIcon');
|
||||
const savedText = document.getElementById('savedText');
|
||||
|
||||
checkIfSaved.then((results) => {
|
||||
if (results === false) {
|
||||
savedText.innerHTML = 'SAVED';
|
||||
saveIcon.classList.remove('far');
|
||||
saveIcon.classList.remove('unsaved');
|
||||
saveIcon.classList.add('fas');
|
||||
saveIcon.classList.add('saved');
|
||||
playerView.savedText = 'FAVORITED';
|
||||
playerView.savedIconType = 'fas saved';
|
||||
addSavedVideo(videoId);
|
||||
} else {
|
||||
savedText.innerHTML = 'SAVE';
|
||||
saveIcon.classList.remove('fas');
|
||||
saveIcon.classList.remove('saved');
|
||||
saveIcon.classList.add('far');
|
||||
saveIcon.classList.add('unsaved');
|
||||
playerView.savedText = 'FAVORITE';
|
||||
playerView.savedIconType = 'far unsaved';
|
||||
removeSavedVideo(videoId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a video was saved in the user's saved video database
|
||||
*
|
||||
* @param {string} videoId - The video ID to check
|
||||
*
|
||||
* @return {promise} - A boolean value if the video was found or not.
|
||||
*/
|
||||
* Checks if a video was saved in the user's saved video database
|
||||
*
|
||||
* @param {string} videoId - The video ID to check
|
||||
*
|
||||
* @return {promise} - A boolean value if the video was found or not.
|
||||
*/
|
||||
function videoIsSaved(videoId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
savedVidsDb.find({videoId: videoId}, (err, docs) => {
|
||||
if (jQuery.isEmptyObject(docs)) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
savedVidsDb.find({
|
||||
videoId: videoId
|
||||
}, (err, docs) => {
|
||||
if (jQuery.isEmptyObject(docs)) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,28 +114,27 @@ function videoIsSaved(videoId) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function showSavedVideos(){
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
//clearMainContainer();
|
||||
//startLoadingAnimation();
|
||||
console.log('checking saved videos');
|
||||
|
||||
let videoList = '';
|
||||
let videoList = '';
|
||||
|
||||
// Check the database for the list of videos.
|
||||
savedVidsDb.find({}).sort({
|
||||
timeSaved: -1
|
||||
}).exec((err, docs) => {
|
||||
// The YouTube API requires a max of 50 videos to be shown. Don't show more than 50.
|
||||
// TODO: Allow the app to show more than 50 saved videos.
|
||||
if(docs.length > 49){
|
||||
for (let i = 0; i < 49; i++) {
|
||||
videoList = videoList + ',' + docs[i].videoId;
|
||||
}
|
||||
}
|
||||
else{
|
||||
docs.forEach((video) => {
|
||||
videoList = videoList + ',' + video.videoId;
|
||||
});
|
||||
}
|
||||
// Check the database for the list of videos.
|
||||
savedVidsDb.find({}).sort({
|
||||
timeSaved: -1
|
||||
}).exec((err, docs) => {
|
||||
// The YouTube API requires a max of 50 videos to be shown. Don't show more than 50.
|
||||
// TODO: Allow the app to show more than 50 saved videos.
|
||||
if (docs.length > 49) {
|
||||
for (let i = 0; i < 49; i++) {
|
||||
videoList = videoList + ',' + docs[i].videoId;
|
||||
}
|
||||
} else {
|
||||
docs.forEach((video) => {
|
||||
videoList = videoList + ',' + video.videoId;
|
||||
});
|
||||
}
|
||||
|
||||
// Call the YouTube API
|
||||
youtubeAPI('videos', {
|
||||
|
@ -151,14 +143,14 @@ function showSavedVideos(){
|
|||
maxResults: 50,
|
||||
}, (data) => {
|
||||
// Render the videos to the screen
|
||||
createVideoListContainer('Saved Videos:');
|
||||
let grabDuration = getDuration(data.items);
|
||||
grabDuration.then((videoList) => {
|
||||
savedView.videoList = [];
|
||||
loadingView.seen = false;
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video, 'saved');
|
||||
});
|
||||
});
|
||||
stopLoadingAnimation();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
@ -22,20 +19,14 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
||||
|
||||
// To any third party devs that fork the project, please be ethical and change the API keys.
|
||||
const apiKeyBank = ['AIzaSyC9E579nh_qqxg6BH4xIce3k_7a9mT4uQc', 'AIzaSyCKplYT6hZIlm2O9FbWTi1G7rkpsLNTq78', 'AIzaSyAE5xzh5GcA_tEDhXmMFd1pEzrL-W7z51E', 'AIzaSyDoFzqwuO9l386eF6BmNkVapjiTJ93CBy4', 'AIzaSyBljfZFPioB0TRJAj-0LS4tlIKl2iucyY4'];
|
||||
const apiKeyBank = ['AIzaSyC9E579nh_qqxg6BH4xIce3k_7a9mT4uQc', 'AIzaSyCKplYT6hZIlm2O9FbWTi1G7rkpsLNTq78', 'AIzaSyAE5xzh5GcA_tEDhXmMFd1pEzrL-W7z51E', 'AIzaSyDoFzqwuO9l386eF6BmNkVapjiTJ93CBy4', 'AIzaSyBljfZFPioB0TRJAj-0LS4tlIKl2iucyY4', 'AIzaSyAiKgR75e3XAznCcb1cj4NUJ5rR_y3uB8E', 'AIzaSyBZL2Ie1masjwbIa74bR2GONF3p518npVU', 'AIzaSyA0CkT2lS1q9HHaFYGNGM4Ycjl1kmRy22s', 'AIzaSyDPy5jq2l1Bgv3-MbpGdZd3W3ik1BMZeDc'];
|
||||
|
||||
/**
|
||||
* Display the settings screen to the user.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function showSettings() {
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
|
||||
let isChecked = '';
|
||||
let key = '';
|
||||
|
||||
function updateSettingsView() {
|
||||
/*
|
||||
* Check the settings database for the user's current settings. This is so the
|
||||
* settings page has the correct toggles related when it is rendered.
|
||||
|
@ -45,7 +36,7 @@ function showSettings() {
|
|||
switch (setting['_id']) {
|
||||
case 'apiKey':
|
||||
if (apiKeyBank.indexOf(setting['value']) == -1) {
|
||||
key = setting['value'];
|
||||
settingsView.apiKey = setting['value'];
|
||||
}
|
||||
break;
|
||||
case 'theme':
|
||||
|
@ -55,28 +46,17 @@ function showSettings() {
|
|||
}
|
||||
});
|
||||
|
||||
// Grab the settings.html template to prepare for rendering
|
||||
const settingsTemplate = require('./templates/settings.html')
|
||||
mustache.parse(settingsTemplate);
|
||||
const rendered = mustache.render(settingsTemplate, {
|
||||
isChecked: isChecked,
|
||||
key: key,
|
||||
});
|
||||
// Render template to application
|
||||
$('#main').html(rendered);
|
||||
stopLoadingAnimation();
|
||||
|
||||
// Check / uncheck the switch depending on the user's settings.
|
||||
if (currentTheme === 'light') {
|
||||
document.getElementById('themeSwitch').checked = false;
|
||||
settingsView.useTheme = false;
|
||||
} else {
|
||||
document.getElementById('themeSwitch').checked = true;
|
||||
settingsView.useTheme = true;
|
||||
}
|
||||
|
||||
if (useTor) {
|
||||
document.getElementById('torSwitch').checked = true;
|
||||
settingsView.useTor = true;
|
||||
} else {
|
||||
document.getElementById('torSwitch').checked = false;
|
||||
settingsView.useTor = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -89,12 +69,12 @@ function showSettings() {
|
|||
function checkDefaultSettings() {
|
||||
|
||||
// Grab a random API Key.
|
||||
apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
|
||||
settingsView.apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
|
||||
let newSetting;
|
||||
|
||||
let settingDefaults = {
|
||||
'theme': 'light',
|
||||
'apiKey': apiKey,
|
||||
'apiKey': settingsView.apiKey,
|
||||
'useTor': false
|
||||
};
|
||||
|
||||
|
@ -121,7 +101,10 @@ function checkDefaultSettings() {
|
|||
break;
|
||||
case 'apiKey':
|
||||
if (apiKeyBank.indexOf(docs[0]['value']) == -1) {
|
||||
apiKey = docs[0]['value'];
|
||||
settingsView.apiKey = docs[0]['value'];
|
||||
}
|
||||
else{
|
||||
settingsView.apiKey = settingDefaults.apiKey;
|
||||
}
|
||||
break;
|
||||
case 'useTor':
|
||||
|
@ -146,7 +129,12 @@ function updateSettings() {
|
|||
let key = document.getElementById('api-key').value;
|
||||
let theme = 'light';
|
||||
|
||||
apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
|
||||
if (apiKeyBank.indexOf(key) == -1 && key !== '') {
|
||||
settingsView.apiKey = key;
|
||||
}
|
||||
else{
|
||||
settingsView.apiKey = apiKeyBank[Math.floor(Math.random() * apiKeyBank.length)];
|
||||
}
|
||||
|
||||
console.log(themeSwitch);
|
||||
|
||||
|
@ -177,20 +165,12 @@ function updateSettings() {
|
|||
useTor = torSwitch;
|
||||
});
|
||||
|
||||
if (key != '') {
|
||||
settingsDb.update({
|
||||
_id: 'apiKey'
|
||||
}, {
|
||||
value: key
|
||||
}, {});
|
||||
} else {
|
||||
// To any third party devs that fork the project, please be ethical and change the API key.
|
||||
settingsDb.update({
|
||||
_id: 'apiKey'
|
||||
}, {
|
||||
value: apiKey
|
||||
}, {});
|
||||
}
|
||||
// To any third party devs that fork the project, please be ethical and change the API key.
|
||||
settingsDb.update({
|
||||
_id: 'apiKey'
|
||||
}, {
|
||||
value: settingsView.apiKey
|
||||
}, {});
|
||||
|
||||
showToast('Settings have been saved.');
|
||||
}
|
||||
|
@ -300,11 +280,6 @@ function importSubscriptions(){
|
|||
let fileType = (i < 0) ? '' : fileLocation[0].substr(i);
|
||||
console.log(fileType);
|
||||
|
||||
/*if (fileType !== '.db'){
|
||||
showToast('Incorrect filetype. Import was unsuccessful.');
|
||||
return;
|
||||
}*/
|
||||
|
||||
fs.readFile(fileLocation[0], function(readErr, data){
|
||||
if(readErr){
|
||||
showToast('Unable to read file. File may be corrupt or have invalid permissions.');
|
||||
|
@ -424,3 +399,5 @@ function clearFile(type, showMessage = true){
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
checkDefaultSettings();
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -21,6 +21,9 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
* File for all functions related to subscriptions.
|
||||
*/
|
||||
|
||||
let subscriptionTimer;
|
||||
let checkSubscriptions = true;
|
||||
|
||||
/**
|
||||
* Add a channel to the user's subscription database.
|
||||
*
|
||||
|
@ -29,30 +32,30 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
* @return {Void}
|
||||
*/
|
||||
function addSubscription(channelId, useToast = true) {
|
||||
console.log(channelId);
|
||||
// Request YouTube API
|
||||
youtubeAPI('channels', {
|
||||
part: 'snippet',
|
||||
id: channelId,
|
||||
}, function(data) {
|
||||
const channelInfo = data['items'][0]['snippet'];
|
||||
const channelName = channelInfo['title'];
|
||||
const thumbnail = channelInfo['thumbnails']['high']['url'];
|
||||
ft.log('Channel ID: ', channelId);
|
||||
// Request YouTube API
|
||||
youtubeAPI('channels', {
|
||||
part: 'snippet',
|
||||
id: channelId,
|
||||
}, (data) => {
|
||||
const channelInfo = data['items'][0]['snippet'];
|
||||
const channelName = channelInfo['title'];
|
||||
const thumbnail = channelInfo['thumbnails']['high']['url'];
|
||||
|
||||
const channel = {
|
||||
channelId: channelId,
|
||||
channelName: channelName,
|
||||
channelThumbnail: thumbnail,
|
||||
};
|
||||
const channel = {
|
||||
channelId: channelId,
|
||||
channelName: channelName,
|
||||
channelThumbnail: thumbnail,
|
||||
};
|
||||
|
||||
// Refresh the list of subscriptions on the side navigation bar.
|
||||
subDb.insert(channel, (err, newDoc) => {
|
||||
if (useToast) {
|
||||
showToast('Added ' + channelName + ' to subscriptions.');
|
||||
displaySubs();
|
||||
}
|
||||
// Refresh the list of subscriptions on the side navigation bar.
|
||||
subDb.insert(channel, (err, newDoc) => {
|
||||
if (useToast) {
|
||||
showToast('Added ' + channelName + ' to subscriptions.');
|
||||
displaySubs();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,13 +66,13 @@ function addSubscription(channelId, useToast = true) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function removeSubscription(channelId) {
|
||||
subDb.remove({
|
||||
channelId: channelId
|
||||
}, {}, (err, numRemoved) => {
|
||||
// Refresh the list of subscriptions on the side navigation bar.
|
||||
displaySubs();
|
||||
showToast('Removed channel from subscriptions.');
|
||||
});
|
||||
subDb.remove({
|
||||
channelId: channelId
|
||||
}, {}, (err, numRemoved) => {
|
||||
// Refresh the list of subscriptions on the side navigation bar.
|
||||
displaySubs();
|
||||
showToast('Removed channel from subscriptions.');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,94 +80,103 @@ function removeSubscription(channelId) {
|
|||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function loadSubscriptions() {
|
||||
clearMainContainer();
|
||||
showToast('Getting Subscriptions. Please wait...');
|
||||
const loading = document.getElementById('loading');
|
||||
function loadSubscriptions() {
|
||||
if (checkSubscriptions === false && subscriptionView.videoList.length > 0){
|
||||
console.log('Will not load subscriptions. Timer still on.');
|
||||
loadingView.seen = false;
|
||||
return;
|
||||
}
|
||||
else{
|
||||
showToast('Refreshing Subscription List. Please wait...');
|
||||
checkSubscriptions = false;
|
||||
}
|
||||
|
||||
startLoadingAnimation()
|
||||
let videoList = [];
|
||||
|
||||
let videoList = [];
|
||||
const subscriptions = returnSubscriptions();
|
||||
|
||||
const subscriptions = returnSubscriptions();
|
||||
subscriptions.then((results) => {
|
||||
let channelId = '';
|
||||
let videoList = [];
|
||||
|
||||
// Welcome to callback hell, we hope you enjoy your stay.
|
||||
subscriptions.then((results) => {
|
||||
let channelId = '';
|
||||
let videoList = [];
|
||||
if (results.length > 0) {
|
||||
let counter = 0;
|
||||
|
||||
if (results.length > 0) {
|
||||
let counter = 0;
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
channelId = results[i]['channelId'];
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
channelId = results[i]['channelId'];
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet',
|
||||
channelId: channelId,
|
||||
type: 'video',
|
||||
maxResults: 15,
|
||||
order: 'date',
|
||||
}, (data) => {
|
||||
console.log(data);
|
||||
videoList = videoList.concat(data.items);
|
||||
counter++;
|
||||
progressView.progressWidth = (counter / results.length) * 100;
|
||||
if (counter === results.length) {
|
||||
videoList.sort((a, b) => {
|
||||
const date1 = Date.parse(a.snippet.publishedAt);
|
||||
const date2 = Date.parse(b.snippet.publishedAt);
|
||||
|
||||
youtubeAPI('search', {
|
||||
part: 'snippet',
|
||||
channelId: channelId,
|
||||
type: 'video',
|
||||
maxResults: 15,
|
||||
order: 'date',
|
||||
}, (data) => {
|
||||
console.log(data);
|
||||
videoList = videoList.concat(data.items);
|
||||
counter++;
|
||||
if (counter === results.length) {
|
||||
videoList.sort((a, b) => {
|
||||
const date1 = Date.parse(a.snippet.publishedAt);
|
||||
const date2 = Date.parse(b.snippet.publishedAt);
|
||||
return date2.valueOf() - date1.valueOf();
|
||||
});
|
||||
|
||||
return date2.valueOf() - date1.valueOf();
|
||||
});
|
||||
// The YouTube website limits the subscriptions to 100 before grabbing more so we only show 100
|
||||
// to keep the app running at a good speed.
|
||||
if (videoList.length < 50) {
|
||||
let grabDuration = getDuration(videoList.slice(0, 49));
|
||||
|
||||
// Render the videos to the application.
|
||||
createVideoListContainer('Latest Subscriptions:');
|
||||
grabDuration.then((list) => {
|
||||
subscriptionView.videoList = [];
|
||||
list.items.forEach((video) => {
|
||||
displayVideo(video, 'subscriptions');
|
||||
});
|
||||
loadingView.seen = false;
|
||||
progressView.seen = false;
|
||||
progressView.progressWidth = 0;
|
||||
});
|
||||
} else {
|
||||
console.log(videoList);
|
||||
let finishedList = [];
|
||||
let firstBatchDuration = getDuration(videoList.slice(0, 49));
|
||||
|
||||
// The YouTube website limits the subscriptions to 100 before grabbing more so we only show 100
|
||||
// to keep the app running at a good speed.
|
||||
if (videoList.length < 50) {
|
||||
let grabDuration = getDuration(videoList.slice(0, 49));
|
||||
firstBatchDuration.then((list1) => {
|
||||
finishedList = finishedList.concat(list1.items);
|
||||
let secondBatchDuration = getDuration(videoList.slice(50, 99));
|
||||
|
||||
grabDuration.then((list) => {
|
||||
list.items.forEach((video) => {
|
||||
displayVideo(video);
|
||||
});
|
||||
stopLoadingAnimation();
|
||||
});
|
||||
} else {
|
||||
console.log(videoList);
|
||||
let finishedList = [];
|
||||
let firstBatchDuration = getDuration(videoList.slice(0, 49));
|
||||
|
||||
firstBatchDuration.then((list1) => {
|
||||
finishedList = finishedList.concat(list1.items);
|
||||
let secondBatchDuration = getDuration(videoList.slice(50, 99));
|
||||
|
||||
secondBatchDuration.then((list2) => {
|
||||
finishedList = finishedList.concat(list2.items);
|
||||
finishedList.forEach((video) => {
|
||||
displayVideo(video);
|
||||
});
|
||||
stopLoadingAnimation();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
secondBatchDuration.then((list2) => {
|
||||
finishedList = finishedList.concat(list2.items);
|
||||
console.log(finishedList);
|
||||
subscriptionView.videoList = [];
|
||||
finishedList.forEach((video) => {
|
||||
displayVideo(video, 'subscriptions');
|
||||
});
|
||||
loadingView.seen = false;
|
||||
progressView.seen = false;
|
||||
progressView.progressWidth = 0;
|
||||
subscriptionTimer = window.setTimeout(() => {
|
||||
checkSubscriptions = true;
|
||||
}, 60000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// User has no subscriptions. Display message.
|
||||
const container = document.getElementById('main');
|
||||
stopLoadingAnimation();
|
||||
|
||||
container.innerHTML = `<h2 class="message">Your Subscription list is currently empty. Start adding subscriptions
|
||||
to see them here.<br /><br /><i class="far fa-frown" style="font-size: 200px"></i></h2>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// User has no subscriptions. Display message.
|
||||
loadingView.seen = false;
|
||||
headerView.seen = false;
|
||||
noSubscriptions.seen = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of subscriptions from the user's subscription database.
|
||||
|
@ -172,11 +184,11 @@ function loadSubscriptions() {
|
|||
* @return {promise} The list of subscriptions.
|
||||
*/
|
||||
function returnSubscriptions() {
|
||||
return new Promise((resolve, reject) => {
|
||||
subDb.find({}, (err, subs) => {
|
||||
resolve(subs);
|
||||
return new Promise((resolve, reject) => {
|
||||
subDb.find({}, (err, subs) => {
|
||||
resolve(subs);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,31 +197,31 @@ function returnSubscriptions() {
|
|||
* @return {Void}
|
||||
*/
|
||||
function displaySubs() {
|
||||
const subList = document.getElementById('subscriptions');
|
||||
const subList = document.getElementById('subscriptions');
|
||||
|
||||
subList.innerHTML = '';
|
||||
subList.innerHTML = '';
|
||||
|
||||
// Sort alphabetically
|
||||
subDb.find({}).sort({
|
||||
channelName: 1
|
||||
}).exec((err, subs) => {
|
||||
subs.forEach((channel) => {
|
||||
// Grab subscriptions.html to be used as a template.
|
||||
const subsTemplate = require('./templates/subscriptions.html')
|
||||
mustache.parse(subsTemplate);
|
||||
const rendered = mustache.render(subsTemplate, {
|
||||
channelIcon: channel['channelThumbnail'],
|
||||
channelName: channel['channelName'],
|
||||
channelId: channel['channelId'],
|
||||
});
|
||||
// Render template to page.
|
||||
const subscriptionsHtml = $('#subscriptions').html();
|
||||
$('#subscriptions').html(subscriptionsHtml + rendered);
|
||||
// Sort alphabetically
|
||||
subDb.find({}).sort({
|
||||
channelName: 1
|
||||
}).exec((err, subs) => {
|
||||
subs.forEach((channel) => {
|
||||
// Grab subscriptions.html to be used as a template.
|
||||
const subsTemplate = require('./templates/subscriptions.html')
|
||||
mustache.parse(subsTemplate);
|
||||
const rendered = mustache.render(subsTemplate, {
|
||||
channelIcon: channel['channelThumbnail'],
|
||||
channelName: channel['channelName'],
|
||||
channelId: channel['channelId'],
|
||||
});
|
||||
// Render template to page.
|
||||
const subscriptionsHtml = $('#subscriptions').html();
|
||||
$('#subscriptions').html(subscriptionsHtml + rendered);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Add onclick function
|
||||
$('#subscriptions .fa-times').onClick = removeSubscription;
|
||||
// Add onclick function
|
||||
$('#subscriptions .fa-times').onClick = removeSubscription;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -219,25 +231,25 @@ function displaySubs() {
|
|||
* @return {Void}
|
||||
*/
|
||||
function toggleSubscription(channelId) {
|
||||
event.stopPropagation();
|
||||
event.stopPropagation();
|
||||
|
||||
const checkIfSubscribed = isSubscribed(channelId);
|
||||
const subscribeButton = document.getElementById('subscribeButton');
|
||||
const checkIfSubscribed = isSubscribed(channelId);
|
||||
const subscribeButton = document.getElementById('subscribeButton');
|
||||
|
||||
checkIfSubscribed.then((results) => {
|
||||
checkIfSubscribed.then((results) => {
|
||||
|
||||
if (results === false) {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'UNSUBSCRIBE';
|
||||
}
|
||||
addSubscription(channelId);
|
||||
} else {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'SUBSCRIBE';
|
||||
}
|
||||
removeSubscription(channelId);
|
||||
}
|
||||
});
|
||||
if (results === false) {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'UNSUBSCRIBE';
|
||||
}
|
||||
addSubscription(channelId);
|
||||
} else {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'SUBSCRIBE';
|
||||
}
|
||||
removeSubscription(channelId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,15 +260,15 @@ function toggleSubscription(channelId) {
|
|||
* @return {promise} - A boolean value if the channel is currently subscribed or not.
|
||||
*/
|
||||
function isSubscribed(channelId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
subDb.find({
|
||||
channelId: channelId
|
||||
}, (err, docs) => {
|
||||
if (jQuery.isEmptyObject(docs)) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
subDb.find({
|
||||
channelId: channelId
|
||||
}, (err, docs) => {
|
||||
if (jQuery.isEmptyObject(docs)) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Vue from './js/vue.js';
|
||||
|
||||
const mainHeaderTemplate = require('./templates/mainHeader.html');
|
||||
const aboutTemplate = require('./templates/about.html');
|
||||
const settingsTemplate = require('./templates/settings.html');
|
||||
const videoListTemplate = require('./templates/videoTemplate.html');
|
||||
const nextPageTemplate = require('./templates/searchNextPage.html');
|
||||
const playerTemplate = require('./templates/player.html');
|
||||
const channelTemplate = require('./templates/channelView.html');
|
||||
const progressViewTemplate = require('./templates/progressView.html');
|
||||
|
||||
/*
|
||||
* Progress view
|
||||
*
|
||||
* Shows progress bar on bottom of application.
|
||||
*
|
||||
* seen: Toggles visibility of view
|
||||
* progressWidth: sets width of the progress bar
|
||||
*/
|
||||
let progressView = new Vue({
|
||||
el: '#progressView',
|
||||
data: {
|
||||
seen: true,
|
||||
progressWidth: 0
|
||||
},
|
||||
template: progressViewTemplate
|
||||
});
|
||||
|
||||
let loadingView = new Vue({
|
||||
el: '#loading',
|
||||
data: {
|
||||
seen: false
|
||||
}
|
||||
});
|
||||
|
||||
let noSubscriptions = new Vue({
|
||||
el: '#noSubscriptions',
|
||||
data: {
|
||||
seen: false
|
||||
}
|
||||
});
|
||||
|
||||
let sideNavBar = new Vue({
|
||||
el: '#sideNav',
|
||||
methods: {
|
||||
subscriptions: (event) => {
|
||||
hideViews();
|
||||
if(subscriptionView.videoList.length === 0){
|
||||
loadingView.seen = true;
|
||||
}
|
||||
headerView.seen = true;
|
||||
headerView.title = 'Latest Subscriptions';
|
||||
subscriptionView.seen = true;
|
||||
loadSubscriptions();
|
||||
},
|
||||
popular: (event) => {
|
||||
hideViews();
|
||||
if (loadingView.seen !== false){
|
||||
loadingView.seen = false;
|
||||
}
|
||||
if(popularView.videoList.length === 0){
|
||||
loadingView.seen = true;
|
||||
}
|
||||
headerView.seen = true;
|
||||
headerView.title = 'Most Popular';
|
||||
popularView.seen = true;
|
||||
showMostPopular();
|
||||
},
|
||||
saved: (event) => {
|
||||
hideViews();
|
||||
if (loadingView.seen !== false){
|
||||
loadingView.seen = false;
|
||||
}
|
||||
if(savedView.videoList.length === 0){
|
||||
loadingView.seen = true;
|
||||
}
|
||||
headerView.seen = true;
|
||||
headerView.title = 'Favorited Videos';
|
||||
savedView.seen = true;
|
||||
showSavedVideos();
|
||||
},
|
||||
history: (event) => {
|
||||
hideViews();
|
||||
if (loadingView.seen !== false){
|
||||
loadingView.seen = false;
|
||||
}
|
||||
if(historyView.videoList.length === 0){
|
||||
loadingView.seen = true;
|
||||
}
|
||||
headerView.seen = true;
|
||||
headerView.title = 'Video History';
|
||||
historyView.seen = true;
|
||||
showHistory();
|
||||
},
|
||||
settings: (event) => {
|
||||
hideViews();
|
||||
if (loadingView.seen !== false){
|
||||
loadingView.seen = false;
|
||||
}
|
||||
settingsView.seen = true;
|
||||
updateSettingsView();
|
||||
},
|
||||
about: (event) => {
|
||||
hideViews();
|
||||
if (loadingView.seen !== false){
|
||||
loadingView.seen = false;
|
||||
}
|
||||
aboutView.seen = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let headerView = new Vue({
|
||||
el: '#mainHeaderView',
|
||||
data: {
|
||||
seen: true,
|
||||
title: 'Latest Subscriptions'
|
||||
},
|
||||
template: mainHeaderTemplate
|
||||
});
|
||||
|
||||
let subscriptionView = new Vue({
|
||||
el: '#subscriptionView',
|
||||
data: {
|
||||
seen: true,
|
||||
isSearch: false,
|
||||
videoList: []
|
||||
},
|
||||
methods: {
|
||||
play: (videoId) => {
|
||||
loadingView.seen = true;
|
||||
playVideo(videoId);
|
||||
},
|
||||
channel: (channelId) => {
|
||||
goToChannel(channelId);
|
||||
},
|
||||
toggleSave: (videoId) => {
|
||||
toggleSavedVideo(videoId);
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
}
|
||||
},
|
||||
template: videoListTemplate
|
||||
});
|
||||
|
||||
let popularView = new Vue({
|
||||
el: '#popularView',
|
||||
data: {
|
||||
seen: false,
|
||||
isSearch: false,
|
||||
videoList: []
|
||||
},
|
||||
methods: {
|
||||
play: (videoId) => {
|
||||
loadingView.seen = true;
|
||||
playVideo(videoId);
|
||||
},
|
||||
channel: (channelId) => {
|
||||
goToChannel(channelId);
|
||||
},
|
||||
toggleSave: (videoId) => {
|
||||
addSavedVideo(videoId);
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
}
|
||||
},
|
||||
template: videoListTemplate
|
||||
});
|
||||
|
||||
let savedView = new Vue({
|
||||
el: '#savedView',
|
||||
data: {
|
||||
seen: false,
|
||||
isSearch: false,
|
||||
videoList: []
|
||||
},
|
||||
methods: {
|
||||
play: (videoId) => {
|
||||
loadingView.seen = true;
|
||||
playVideo(videoId);
|
||||
},
|
||||
channel: (channelId) => {
|
||||
goToChannel(channelId);
|
||||
},
|
||||
toggleSave: (videoId) => {
|
||||
addSavedVideo(videoId);
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
}
|
||||
},
|
||||
template: videoListTemplate
|
||||
});
|
||||
|
||||
let historyView = new Vue({
|
||||
el: '#historyView',
|
||||
data: {
|
||||
seen: false,
|
||||
isSearch: false,
|
||||
videoList: []
|
||||
},
|
||||
methods: {
|
||||
play: (videoId) => {
|
||||
loadingView.seen = true;
|
||||
playVideo(videoId);
|
||||
},
|
||||
channel: (channelId) => {
|
||||
goToChannel(channelId);
|
||||
},
|
||||
toggleSave: (videoId) => {
|
||||
addSavedVideo(videoId);
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
}
|
||||
},
|
||||
template: videoListTemplate
|
||||
});
|
||||
|
||||
let aboutView = new Vue({
|
||||
el: '#aboutView',
|
||||
data: {
|
||||
seen: false,
|
||||
versionNumber: electron.remote.app.getVersion()
|
||||
},
|
||||
template: aboutTemplate
|
||||
});
|
||||
|
||||
let settingsView = new Vue({
|
||||
el: '#settingsView',
|
||||
data: {
|
||||
seen: false,
|
||||
useTheme: false,
|
||||
useTor: false,
|
||||
apiKey: ''
|
||||
},
|
||||
template: settingsTemplate
|
||||
});
|
||||
|
||||
let searchView = new Vue({
|
||||
el: '#searchView',
|
||||
data: {
|
||||
seen: false,
|
||||
isSearch: true,
|
||||
nextPageToken: '',
|
||||
videoList: []
|
||||
},
|
||||
methods: {
|
||||
play: (videoId) => {
|
||||
loadingView.seen = true;
|
||||
playVideo(videoId);
|
||||
},
|
||||
channel: (channelId) => {
|
||||
goToChannel(channelId);
|
||||
},
|
||||
toggleSave: (videoId) => {
|
||||
addSavedVideo(videoId);
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
},
|
||||
nextPage: (nextPageToken) => {
|
||||
console.log(searchView.nextPageToken);
|
||||
search(searchView.nextPageToken);
|
||||
}
|
||||
},
|
||||
template: videoListTemplate
|
||||
});
|
||||
|
||||
let channelView = new Vue({
|
||||
el: '#channelView',
|
||||
data: {
|
||||
seen: false,
|
||||
id: '',
|
||||
name: '',
|
||||
icon: '',
|
||||
baner: '',
|
||||
subCount: '',
|
||||
subButtonText: '',
|
||||
description: ''
|
||||
},
|
||||
methods: {
|
||||
subscription: (channelId) => {
|
||||
toggleSubscription(channelId);
|
||||
},
|
||||
},
|
||||
template: channelTemplate
|
||||
});
|
||||
|
||||
let channelVideosView = new Vue({
|
||||
el: '#channelVideosView',
|
||||
data: {
|
||||
seen: false,
|
||||
isSearch: false,
|
||||
videoList: []
|
||||
},
|
||||
methods: {
|
||||
play: (videoId) => {
|
||||
loadingView.seen = true;
|
||||
playVideo(videoId);
|
||||
},
|
||||
channel: (channelId) => {
|
||||
goToChannel(channelId);
|
||||
},
|
||||
toggleSave: (videoId) => {
|
||||
addSavedVideo(videoId);
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
},
|
||||
},
|
||||
template: videoListTemplate
|
||||
});
|
||||
|
||||
let playerView = new Vue({
|
||||
el: '#playerView',
|
||||
data: {
|
||||
seen: false,
|
||||
publishedDate: '',
|
||||
videoUrl: '',
|
||||
videoId: '',
|
||||
channelId: '',
|
||||
channelIcon: '',
|
||||
channelName: '',
|
||||
subscribedText: '',
|
||||
savedText: '',
|
||||
savedIconType: 'far',
|
||||
description: '',
|
||||
videoThumbnail: '',
|
||||
subtitleHtml: '',
|
||||
currentQuality: '',
|
||||
video480p: '',
|
||||
video720p: '',
|
||||
embededHtml: '',
|
||||
currentSpeed: 1,
|
||||
videoTitle: '',
|
||||
videoViews: '',
|
||||
likePercentage: 0,
|
||||
videoLikes: 0,
|
||||
videoDislikes: 0,
|
||||
playerSeen: true,
|
||||
recommendedVideoList: []
|
||||
},
|
||||
methods: {
|
||||
channel: (channelId) => {
|
||||
goToChannel(channelId);
|
||||
},
|
||||
subscription: (videoId) => {
|
||||
toggleSubscription(videoId);
|
||||
},
|
||||
quality: (url, qualityText) => {
|
||||
console.log(url);
|
||||
console.log(qualityText);
|
||||
if(playerView.playerSeen === true){
|
||||
// Update time to new url
|
||||
const currentPlayBackTime = $('.videoPlayer').get(0).currentTime;
|
||||
console.log(currentPlayBackTime);
|
||||
playerView.videoUrl = url;
|
||||
playerView.currentQuality = qualityText;
|
||||
setTimeout(() => {$('.videoPlayer').get(0).currentTime = currentPlayBackTime;}, 100);
|
||||
}
|
||||
else{
|
||||
playerView.playerSeen = true;
|
||||
playerView.videoUrl = url;
|
||||
playerView.currentQuality = qualityText;
|
||||
}
|
||||
},
|
||||
embededPlayer: () => {
|
||||
playerView.playerSeen = false;
|
||||
playerView.currentQuality = 'EMBED';
|
||||
},
|
||||
copy: (site, videoId) => {
|
||||
const url = 'https://' + site + '/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
},
|
||||
save: (videoId) => {
|
||||
toggleSavedVideo(videoId);
|
||||
},
|
||||
play: (videoId) => {
|
||||
loadingView.seen = true;
|
||||
playVideo(videoId);
|
||||
}
|
||||
},
|
||||
template: playerTemplate
|
||||
});
|
||||
|
||||
function hideViews(){
|
||||
subscriptionView.seen = false;
|
||||
noSubscriptions.seen = false;
|
||||
aboutView.seen = false;
|
||||
headerView.seen = false;
|
||||
searchView.seen = false;
|
||||
settingsView.seen = false;
|
||||
popularView.seen = false;
|
||||
savedView.seen = false;
|
||||
historyView.seen = false;
|
||||
playerView.seen = false;
|
||||
channelView.seen = false;
|
||||
channelVideosView.seen = false;
|
||||
}
|
|
@ -1,45 +1,45 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
// import {freeTubeLog} from './events.js';
|
||||
|
||||
/*
|
||||
* A file for checking / managing updates
|
||||
*/
|
||||
|
||||
const updateChecker = require('github-version-checker');
|
||||
const updateChecker = require('github-version-checker');
|
||||
|
||||
const options = {
|
||||
token: 'USERACCESSTOKEN', // personal access token. Github will not allow commiting the access token, which is why this is blank.
|
||||
repo: 'freetube', // repository name
|
||||
owner: 'freetubeapp', // repository owner
|
||||
currentVersion: require('electron').remote.app.getVersion(), // your app's current version
|
||||
fetchTags: false // whether to fetch releases or tags
|
||||
};
|
||||
const options = {
|
||||
token: 'USERACCESSTOKEN', // personal access token. Github will not allow commiting the access token, which is why this is blank.
|
||||
repo: 'freetube', // repository name
|
||||
owner: 'freetubeapp', // repository owner
|
||||
currentVersion: require('electron').remote.app.getVersion(), // your app's current version
|
||||
fetchTags: false // whether to fetch releases or tags
|
||||
};
|
||||
|
||||
const openReleasePage = function(){
|
||||
shell.openExternal('https://github.com/FreeTubeApp/FreeTube/releases');
|
||||
}
|
||||
const openReleasePage = function () {
|
||||
shell.openExternal('https://github.com/FreeTubeApp/FreeTube/releases');
|
||||
}
|
||||
|
||||
/*function checkForUpdates() {
|
||||
updateChecker(options, function(error, update) { // callback function
|
||||
if (error){
|
||||
showToast('There was a problem with checking for updates');
|
||||
console.log(error);
|
||||
freeTubeLog(error);
|
||||
}
|
||||
if (update) { // print some update info if an update is available
|
||||
confirmFunction(update.name + ' is now available! Would you like to download the update?', openReleasePage);
|
||||
|
@ -50,9 +50,9 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
});
|
||||
}*/
|
||||
|
||||
updateChecker(options, function(error, update) { // callback function
|
||||
if (error) throw error;
|
||||
if (update) { // print some update info if an update is available
|
||||
confirmFunction(update.name + ' is now available! Would you like to download the update?', openReleasePage);
|
||||
}
|
||||
});
|
||||
updateChecker(options, function (error, update) { // callback function
|
||||
if (error) throw error;
|
||||
if (update) { // print some update info if an update is available
|
||||
confirmFunction(update.name + ' is now available! Would you like to download the update?', openReleasePage);
|
||||
}
|
||||
});
|
719
src/js/videos.js
719
src/js/videos.js
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -25,70 +25,72 @@ along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
|||
* @return {Void}
|
||||
*/
|
||||
function search(nextPageToken = '') {
|
||||
const query = document.getElementById('search').value;
|
||||
const query = document.getElementById('search').value;
|
||||
|
||||
if (query === '') {
|
||||
return;
|
||||
}
|
||||
if (query === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextPageToken === '') {
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
hideViews();
|
||||
headerView.seen = true;
|
||||
headerView.title = 'Search Results';
|
||||
searchView.videoList = [];
|
||||
searchView.seen = true;
|
||||
} else {
|
||||
console.log(nextPageToken);
|
||||
showToast('Fetching results. Please wait...');
|
||||
}
|
||||
|
||||
youtubeAPI('search', {
|
||||
q: query,
|
||||
part: 'id',
|
||||
pageToken: nextPageToken,
|
||||
maxResults: 25,
|
||||
}, function(data) {
|
||||
console.log(data);
|
||||
youtubeAPI('search', {
|
||||
q: query,
|
||||
part: 'id',
|
||||
pageToken: nextPageToken,
|
||||
maxResults: 25,
|
||||
}, function (data) {
|
||||
ft.log('Search Data: ', data);
|
||||
|
||||
let channels = data.items.filter((item) => {
|
||||
if (item.id.kind === 'youtube#channel') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
let channels = data.items.filter((item) => {
|
||||
if (item.id.kind === 'youtube#channel') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
let playlists = data.items.filter((item) => {
|
||||
if (item.id.kind === 'youtube#playlist') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
let playlists = data.items.filter((item) => {
|
||||
if (item.id.kind === 'youtube#playlist') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
let videos = data.items.filter((item) => {
|
||||
if (item.id.kind === 'youtube#video') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
let videos = data.items.filter((item) => {
|
||||
if (item.id.kind === 'youtube#video') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(channels);
|
||||
console.log(typeof(channels));
|
||||
console.log(playlists);
|
||||
ft.log('Channels: ', channels);
|
||||
ft.log('Typeof object above (channels) ^^', typeof (channels));
|
||||
ft.log('Playlists', playlists);
|
||||
|
||||
if(playlists.length > 0){
|
||||
//displayPlaylists(playlists);
|
||||
}
|
||||
if (playlists.length > 0) {
|
||||
//displayPlaylists(playlists);
|
||||
}
|
||||
|
||||
if(channels.length > 0){
|
||||
displayChannels(channels);
|
||||
}
|
||||
if (channels.length > 0) {
|
||||
displayChannels(channels);
|
||||
}
|
||||
|
||||
let grabDuration = getDuration(videos);
|
||||
let grabDuration = getDuration(videos);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
console.log(videoList);
|
||||
videoList.items.forEach(displayVideo);
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video, 'search');
|
||||
});
|
||||
});
|
||||
|
||||
if (nextPageToken === '') {
|
||||
createVideoListContainer('Search results:');
|
||||
stopLoadingAnimation();
|
||||
}
|
||||
addNextPage(data.nextPageToken);
|
||||
searchView.nextPageToken = data.nextPageToken;
|
||||
loadingView.seen = false;
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -100,32 +102,32 @@ function search(nextPageToken = '') {
|
|||
* @return {promise} - The list of videos with the duration included.
|
||||
*/
|
||||
function getDuration(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let videoIdList = '';
|
||||
return new Promise((resolve, reject) => {
|
||||
let videoIdList = '';
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (videoIdList === '') {
|
||||
if (typeof(data[i]['id']) === 'string') {
|
||||
videoIdList = data[i]['id'];
|
||||
} else {
|
||||
videoIdList = data[i]['id']['videoId'];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (videoIdList === '') {
|
||||
if (typeof (data[i]['id']) === 'string') {
|
||||
videoIdList = data[i]['id'];
|
||||
} else {
|
||||
videoIdList = data[i]['id']['videoId'];
|
||||
}
|
||||
} else {
|
||||
if (typeof (data[i]['id']) === 'string') {
|
||||
videoIdList = videoIdList + ', ' + data[i]['id'];
|
||||
} else {
|
||||
videoIdList = videoIdList + ', ' + data[i]['id']['videoId'];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (typeof(data[i]['id']) === 'string') {
|
||||
videoIdList = videoIdList + ', ' + data[i]['id'];
|
||||
} else {
|
||||
videoIdList = videoIdList + ', ' + data[i]['id']['videoId'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
youtubeAPI('videos', {
|
||||
part: 'snippet, contentDetails',
|
||||
id: videoIdList
|
||||
}, (data) => {
|
||||
resolve(data);
|
||||
youtubeAPI('videos', {
|
||||
part: 'snippet, contentDetails',
|
||||
id: videoIdList
|
||||
}, (data) => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,21 +139,21 @@ function getDuration(data) {
|
|||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function displayVideo(video, listType = '') {
|
||||
const videoSnippet = video.snippet;
|
||||
function displayVideo(videoData, listType = '') {
|
||||
let video = {};
|
||||
|
||||
const videoDuration = parseVideoDuration(video.contentDetails.duration);
|
||||
//const videoDuration = '00:00';
|
||||
const videoSnippet = videoData.snippet;
|
||||
|
||||
video.duration = parseVideoDuration(videoData.contentDetails.duration);
|
||||
|
||||
// Grab the published date for the video and convert to a user readable state.
|
||||
const dateString = new Date(videoSnippet.publishedAt);
|
||||
const publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
video.publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
|
||||
const searchMenu = $('#videoListContainer').html();
|
||||
const videoId = video.id;
|
||||
|
||||
// Include a remove icon in the list if the application is displaying the history list or saved videos.
|
||||
const deleteHtml = () => {
|
||||
video.deleteHtml = () => {
|
||||
switch (listType) {
|
||||
case 'saved':
|
||||
return `<li onclick="removeSavedVideo('${videoId}'); showSavedVideos();">Remove Saved Video</li>`;
|
||||
|
@ -160,113 +162,128 @@ function displayVideo(video, listType = '') {
|
|||
}
|
||||
};
|
||||
|
||||
video.id = videoData.id;
|
||||
video.youtubeUrl = 'https://youtube.com/watch?v=' + video.id;
|
||||
video.invidiousUrl = 'https://invidio.us/watch?v=' + video.id;
|
||||
// Includes text if the video is live.
|
||||
const liveText = (videoSnippet.liveBroadcastContent === 'live') ? 'LIVE NOW' : '';
|
||||
const videoListTemplate = require('./templates/videoList.html');
|
||||
video.liveText = (videoSnippet.liveBroadcastContent === 'live') ? 'LIVE NOW' : '';
|
||||
video.thumbnail = videoSnippet.thumbnails.medium.url;
|
||||
video.title = videoSnippet.title;
|
||||
video.channelName = videoSnippet.channelTitle;
|
||||
video.channelId = videoSnippet.channelId;
|
||||
video.description = videoSnippet.description;
|
||||
video.isVideo = true;
|
||||
|
||||
mustache.parse(videoListTemplate);
|
||||
const rendered = mustache.render(videoListTemplate, {
|
||||
videoId: videoId,
|
||||
videoThumbnail: videoSnippet.thumbnails.medium.url,
|
||||
videoTitle: videoSnippet.title,
|
||||
channelName: videoSnippet.channelTitle,
|
||||
videoDescription: videoSnippet.description,
|
||||
channelId: videoSnippet.channelId,
|
||||
videoDuration: videoDuration,
|
||||
publishedDate: publishedDate,
|
||||
liveText: liveText,
|
||||
deleteHtml: deleteHtml,
|
||||
});
|
||||
|
||||
// Apply the render to the page
|
||||
const nextButton = document.getElementById('getNextPage');
|
||||
if (nextButton === null) {
|
||||
$('#videoListContainer').append(rendered);
|
||||
} else {
|
||||
$(rendered).insertBefore('#getNextPage');
|
||||
switch (listType) {
|
||||
case 'subscriptions':
|
||||
subscriptionView.videoList = subscriptionView.videoList.concat(video);
|
||||
video.removeFromSave = true;
|
||||
break;
|
||||
case 'search':
|
||||
searchView.videoList = searchView.videoList.concat(video);
|
||||
video.removeFromSave = false;
|
||||
break;
|
||||
case 'popular':
|
||||
popularView.videoList = popularView.videoList.concat(video);
|
||||
video.removeFromSave = false;
|
||||
break;
|
||||
case 'saved':
|
||||
savedView.videoList = savedView.videoList.concat(video);
|
||||
video.removeFromSave = false;
|
||||
break;
|
||||
case 'history':
|
||||
historyView.videoList = historyView.videoList.concat(video);
|
||||
video.removeFromSave = false;
|
||||
break;
|
||||
case 'channel':
|
||||
channelVideosView.videoList = channelVideosView.videoList.concat(video);
|
||||
video.removeFromSave = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function displayChannels(channels) {
|
||||
let channelIds;
|
||||
let channelIds;
|
||||
|
||||
channels.forEach((channel) => {
|
||||
if (typeof(channelIds) === 'undefined') {
|
||||
channelIds = channel.id.channelId;
|
||||
} else {
|
||||
channelIds = channelIds + ',' + channel.id.channelId;
|
||||
}
|
||||
});
|
||||
channels.forEach((channel) => {
|
||||
if (typeof (channelIds) === 'undefined') {
|
||||
channelIds = channel.id.channelId;
|
||||
} else {
|
||||
channelIds = channelIds + ',' + channel.id.channelId;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(channelIds);
|
||||
ft.log('Channel IDs: ', channelIds);
|
||||
|
||||
youtubeAPI('channels', {
|
||||
part: 'snippet,statistics',
|
||||
id: channelIds,
|
||||
}, function(data) {
|
||||
console.log(data);
|
||||
let items = data['items'].reverse();
|
||||
const videoListTemplate = require('./templates/channelList.html');
|
||||
youtubeAPI('channels', {
|
||||
part: 'snippet,statistics',
|
||||
id: channelIds,
|
||||
}, function (data) {
|
||||
ft.log('Channel Data: ', data);
|
||||
let items = data['items'].reverse();
|
||||
|
||||
console.log(items);
|
||||
ft.log('Channel Items: ', items);
|
||||
|
||||
items.forEach((item) => {
|
||||
mustache.parse(videoListTemplate);
|
||||
let rendered = mustache.render(videoListTemplate, {
|
||||
channelId: item.id,
|
||||
channelThumbnail: item.snippet.thumbnails.medium.url,
|
||||
channelName: item.snippet.title,
|
||||
channelDescription: item.snippet.description,
|
||||
subscriberCount: item.statistics.subscriberCount,
|
||||
videoCount: item.statistics.videoCount,
|
||||
});
|
||||
let channelData = {};
|
||||
|
||||
$(rendered).insertBefore('#getNextPage');
|
||||
channelData.channelId = item.id;
|
||||
channelData.thumbnail = item.snippet.thumbnails.medium.url;
|
||||
channelData.channelName = item.snippet.title;
|
||||
channelData.description = item.snippet.description;
|
||||
channelData.subscriberCount = item.statistics.subscriberCount;
|
||||
channelData.videoCount = item.statistics.videoCount;
|
||||
channelData.isVideo = false;
|
||||
|
||||
console.log(searchView.videoList);
|
||||
console.log(channelData);
|
||||
|
||||
searchView.videoList = searchView.videoList.concat(channelData);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function displayPlaylists(playlists) {
|
||||
let playlistIds;
|
||||
let playlistIds;
|
||||
|
||||
playlists.forEach((playlist) => {
|
||||
if (typeof(playlistIds) === 'undefined') {
|
||||
playlistIds = playlist.id.playlistId;
|
||||
} else {
|
||||
playlistIds = playlistIds + ',' + playlist.id.playlistId;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(playlistIds);
|
||||
|
||||
youtubeAPI('playlists', {
|
||||
part: 'snippet,contentDetails',
|
||||
id: playlistIds,
|
||||
}, function(data) {
|
||||
console.log(data);
|
||||
let items = data['items'].reverse();
|
||||
const playlistListTemplate = require('./templates/playlistList.html');
|
||||
|
||||
console.log(items);
|
||||
|
||||
items.forEach((item) => {
|
||||
let dateString = new Date(item.snippet.publishedAt);
|
||||
let publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
|
||||
mustache.parse(playlistListTemplate);
|
||||
let rendered = mustache.render(playlistListTemplate, {
|
||||
channelId: item.snippet.channelId,
|
||||
channelName: item.snippet.channelTitle,
|
||||
playlistThumbnail: item.snippet.thumbnails.medium.url,
|
||||
playlistTitle: item.snippet.title,
|
||||
playlistDescription: item.snippet.description,
|
||||
videoCount: item.contentDetails.itemCount,
|
||||
publishedDate: publishedDate,
|
||||
});
|
||||
|
||||
$(rendered).insertBefore('#getNextPage');
|
||||
playlists.forEach((playlist) => {
|
||||
if (typeof (playlistIds) === 'undefined') {
|
||||
playlistIds = playlist.id.playlistId;
|
||||
} else {
|
||||
playlistIds = playlistIds + ',' + playlist.id.playlistId;
|
||||
}
|
||||
});
|
||||
|
||||
ft.log('Playlist IDs: ', playlistIds);
|
||||
|
||||
youtubeAPI('playlists', {
|
||||
part: 'snippet,contentDetails',
|
||||
id: playlistIds,
|
||||
}, function (data) {
|
||||
ft.log('Playlist Data: ', data);
|
||||
let items = data['items'].reverse();
|
||||
const playlistListTemplate = require('./templates/playlistList.html');
|
||||
|
||||
ft.log('Playlist Items: ', items);
|
||||
|
||||
items.forEach((item) => {
|
||||
let dateString = new Date(item.snippet.publishedAt);
|
||||
let publishedDate = dateFormat(dateString, "mmm dS, yyyy");
|
||||
|
||||
mustache.parse(playlistListTemplate);
|
||||
let rendered = mustache.render(playlistListTemplate, {
|
||||
channelId: item.snippet.channelId,
|
||||
channelName: item.snippet.channelTitle,
|
||||
playlistThumbnail: item.snippet.thumbnails.medium.url,
|
||||
playlistTitle: item.snippet.title,
|
||||
playlistDescription: item.snippet.description,
|
||||
videoCount: item.contentDetails.itemCount,
|
||||
publishedDate: publishedDate,
|
||||
});
|
||||
|
||||
$(rendered).insertBefore('#getNextPage');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -277,22 +294,22 @@ function displayPlaylists(playlists) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function addNextPage(nextPageToken) {
|
||||
let oldFetchButton = document.getElementById('getNextPage');
|
||||
let oldFetchButton = document.getElementById('getNextPage');
|
||||
|
||||
// Creates the element if it doesn't exist.
|
||||
if (oldFetchButton === null) {
|
||||
let fetchButton = document.createElement('div');
|
||||
fetchButton.id = 'getNextPage';
|
||||
fetchButton.innerHTML = '<i class="fas fa-search"></i> Fetch more results...';
|
||||
// Creates the element if it doesn't exist.
|
||||
if (oldFetchButton === null) {
|
||||
let fetchButton = document.createElement('div');
|
||||
fetchButton.id = 'getNextPage';
|
||||
fetchButton.innerHTML = '<i class="fas fa-search"></i> Fetch more results...';
|
||||
|
||||
$('#videoListContainer').append(fetchButton);
|
||||
}
|
||||
$('#videoListContainer').append(fetchButton);
|
||||
}
|
||||
|
||||
// Update the on click method of the button.
|
||||
$(document).off('click', '#getNextPage');
|
||||
$(document).on('click', '#getNextPage', (event) => {
|
||||
search(nextPageToken);
|
||||
});
|
||||
// Update the on click method of the button.
|
||||
$(document).off('click', '#getNextPage');
|
||||
$(document).on('click', '#getNextPage', (event) => {
|
||||
search(nextPageToken);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,6 +320,8 @@ function addNextPage(nextPageToken) {
|
|||
* @param {string} videoId - The video ID of the video to get recommendations from.
|
||||
*/
|
||||
function showVideoRecommendations(videoId) {
|
||||
playerView.recommendedVideoList = [];
|
||||
|
||||
youtubeAPI('search', {
|
||||
part: 'id',
|
||||
type: 'video',
|
||||
|
@ -312,21 +331,17 @@ function showVideoRecommendations(videoId) {
|
|||
let grabDuration = getDuration(data.items);
|
||||
grabDuration.then((videoList) => {
|
||||
videoList.items.forEach((video) => {
|
||||
let data = {}
|
||||
const snippet = video.snippet;
|
||||
const videoDuration = parseVideoDuration(video.contentDetails.duration);
|
||||
|
||||
const recommTemplate = require('./templates/recommendations.html')
|
||||
mustache.parse(recommTemplate);
|
||||
const rendered = mustache.render(recommTemplate, {
|
||||
videoId: video.id,
|
||||
videoTitle: snippet.title,
|
||||
channelName: snippet.channelTitle,
|
||||
videoThumbnail: snippet.thumbnails.medium.url,
|
||||
videoDuration: videoDuration,
|
||||
publishedDate: dateFormat(snippet.publishedAt, "mmm dS, yyyy")
|
||||
});
|
||||
const recommendationHtml = $('#recommendations').html();
|
||||
$('#recommendations').html(recommendationHtml + rendered);
|
||||
data.duration = parseVideoDuration(video.contentDetails.duration);
|
||||
data.id = video.id;
|
||||
data.title = snippet.title;
|
||||
data.channelName = snippet.channelTitle;
|
||||
data.thumbnail = snippet.thumbnails.medium.url;
|
||||
data.publishedDate = dateFormat(snippet.publishedAt, "mmm dS, yyyy");
|
||||
|
||||
playerView.recommendedVideoList = playerView.recommendedVideoList.concat(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -339,34 +354,52 @@ function showVideoRecommendations(videoId) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function parseSearchText(url = '') {
|
||||
let input;
|
||||
let input;
|
||||
|
||||
if (url === ''){
|
||||
input = document.getElementById('search').value;
|
||||
}
|
||||
else{
|
||||
input = url;
|
||||
}
|
||||
if (url === '') {
|
||||
input = document.getElementById('search').value;
|
||||
} else {
|
||||
input = url;
|
||||
}
|
||||
|
||||
if (input === '') {
|
||||
return;
|
||||
}
|
||||
if (input === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// The regex to get the video id from a YouTube link. Thanks StackOverflow.
|
||||
let rx = /^.*(?:(?:(you|hook)tu\.?be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/;
|
||||
// The regex to get the video id from a YouTube link. Thanks StackOverflow.
|
||||
let rx = /^.*(?:(?:(you|hook)tu\.?be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/;
|
||||
|
||||
let match = input.match(rx);
|
||||
let match = input.match(rx);
|
||||
|
||||
ft.log('Video ID: ', match);
|
||||
let urlSplit = input.split('/');
|
||||
if (match) {
|
||||
ft.log('Video found');
|
||||
loadingView.seen = true;
|
||||
playVideo(match[2]);
|
||||
} else if (urlSplit[3] == 'channel') {
|
||||
ft.log('channel found');
|
||||
loadingView.seen = true;
|
||||
goToChannel(urlSplit[4]);
|
||||
} else if (urlSplit[3] == 'user') {
|
||||
ft.log('user found');
|
||||
// call api to get the ID and then call goToChannel(id)
|
||||
youtubeAPI('channels', {
|
||||
part: 'id',
|
||||
forUsername: urlSplit[4]
|
||||
}, (data) => {
|
||||
ft.log('Channel Data: ', data.items[0].id);
|
||||
let channelID = data.items[0].id;
|
||||
loadingView.seen = true;
|
||||
goToChannel(channelID);
|
||||
});
|
||||
} else {
|
||||
ft.log('Video not found');
|
||||
loadingView.seen = true;
|
||||
search();
|
||||
}
|
||||
|
||||
console.log(match);
|
||||
|
||||
// Play video if a match is found.
|
||||
try {
|
||||
console.log('Match Found');
|
||||
playVideo(match[2]);
|
||||
} catch (err) {
|
||||
console.log('Video not found');
|
||||
search();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -377,42 +410,42 @@ function parseSearchText(url = '') {
|
|||
* @return {string} - The formated string. Ex: 12:34:56
|
||||
*/
|
||||
function parseVideoDuration(durationString) {
|
||||
let match = durationString.match(/PT(\d+H)?(\d+M)?(\d+S)?/);
|
||||
let duration = '';
|
||||
let match = durationString.match(/P.*T(\d+H)?(\d+M)?(\d+S)?/);
|
||||
let duration = '';
|
||||
|
||||
match = match.slice(1).map(function(x) {
|
||||
if (x != null) {
|
||||
return x.replace(/\D/, '');
|
||||
match = match.slice(1).map(function (x) {
|
||||
if (x != null) {
|
||||
return x.replace(/\D/, '');
|
||||
}
|
||||
});
|
||||
|
||||
let hours = (parseInt(match[0]) || 0);
|
||||
let minutes = (parseInt(match[1]) || 0);
|
||||
let seconds = (parseInt(match[2]) || 0);
|
||||
|
||||
if (hours != 0) {
|
||||
duration = hours + ':';
|
||||
} else {
|
||||
duration = minutes + ':';
|
||||
}
|
||||
});
|
||||
|
||||
let hours = (parseInt(match[0]) || 0);
|
||||
let minutes = (parseInt(match[1]) || 0);
|
||||
let seconds = (parseInt(match[2]) || 0);
|
||||
if (hours != 0 && minutes < 10) {
|
||||
duration = duration + '0' + minutes + ':';
|
||||
} else if (hours != 0 && minutes > 10) {
|
||||
duration = duration + minutes + ':';
|
||||
} else if (hours != 0 && minutes == 0) {
|
||||
duration = duration + '00:';
|
||||
}
|
||||
|
||||
if (hours != 0) {
|
||||
duration = hours + ':';
|
||||
} else {
|
||||
duration = minutes + ':';
|
||||
}
|
||||
if (seconds == 0) {
|
||||
duration = duration + '00';
|
||||
} else if (seconds < 10) {
|
||||
duration = duration + '0' + seconds;
|
||||
} else {
|
||||
duration = duration + seconds;
|
||||
}
|
||||
|
||||
if (hours != 0 && minutes < 10) {
|
||||
duration = duration + '0' + minutes + ':';
|
||||
} else if (hours != 0 && minutes > 10) {
|
||||
duration = duration + minutes + ':';
|
||||
} else if (hours != 0 && minutes == 0) {
|
||||
duration = duration + '00:';
|
||||
}
|
||||
|
||||
if (seconds == 0) {
|
||||
duration = duration + '00';
|
||||
} else if (seconds < 10) {
|
||||
duration = duration + '0' + seconds;
|
||||
} else {
|
||||
duration = duration + seconds;
|
||||
}
|
||||
|
||||
return duration;
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -421,12 +454,10 @@ function parseVideoDuration(durationString) {
|
|||
* @return {Void}
|
||||
*/
|
||||
function showMostPopular() {
|
||||
clearMainContainer();
|
||||
startLoadingAnimation();
|
||||
|
||||
// Get the date of 2 days ago.
|
||||
var d = new Date();
|
||||
d.setDate(d.getDate() - 2);
|
||||
// Get the date of 2 days ago.
|
||||
var d = new Date();
|
||||
d.setDate(d.getDate() - 2);
|
||||
|
||||
// Grab all videos published 2 days ago and after and order them by view count.
|
||||
// These are the videos that are considered as 'most popular' and is how similar
|
||||
|
@ -439,20 +470,23 @@ function showMostPopular() {
|
|||
publishedAfter: d.toISOString(),
|
||||
maxResults: 50,
|
||||
}, function(data) {
|
||||
createVideoListContainer('Most Popular:');
|
||||
//createVideoListContainer('Most Popular:');
|
||||
console.log(data);
|
||||
let grabDuration = getDuration(data.items);
|
||||
|
||||
grabDuration.then((videoList) => {
|
||||
console.log(videoList);
|
||||
videoList.items.forEach(displayVideo);
|
||||
popularView.videoList = [];
|
||||
loadingView.seen = false;
|
||||
videoList.items.forEach((video) => {
|
||||
displayVideo(video, 'popular');
|
||||
});
|
||||
});
|
||||
stopLoadingAnimation();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a link of the video to HookTube or YouTube and copy it to the user's clipboard.
|
||||
* Create a link of the video to Invidious or YouTube and copy it to the user's clipboard.
|
||||
*
|
||||
* @param {string} website - The website to watch the video on.
|
||||
* @param {string} videoId - The video ID of the video to add to the URL
|
||||
|
@ -460,10 +494,21 @@ function showMostPopular() {
|
|||
* @return {Void}
|
||||
*/
|
||||
function copyLink(website, videoId) {
|
||||
// Create the URL and copy to the clipboard.
|
||||
const url = 'https://' + website + '.com/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
// Create the URL and copy to the clipboard.
|
||||
if (website == "youtube") {
|
||||
const url = 'https://' + website + '.com/watch?v=' + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
}
|
||||
|
||||
if (website == "invidious") {
|
||||
website = "invidio";
|
||||
const url = "https://" + website + ".us/watch?v=" + videoId;
|
||||
clipboard.writeText(url);
|
||||
showToast('URL has been copied to the clipboard');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -474,20 +519,20 @@ function copyLink(website, videoId) {
|
|||
* @return {promise} - The HTML of the embeded player
|
||||
*/
|
||||
function getChannelAndPlayer(videoId) {
|
||||
console.log(videoId);
|
||||
return new Promise((resolve, reject) => {
|
||||
youtubeAPI('videos', {
|
||||
part: 'snippet,player',
|
||||
id: videoId,
|
||||
}, function(data) {
|
||||
let embedHtml = data.items[0].player.embedHtml;
|
||||
embedHtml = embedHtml.replace('src="', 'src="https:');
|
||||
embedHtml = embedHtml.replace('width="480px"', '');
|
||||
embedHtml = embedHtml.replace('height="270px"', '');
|
||||
embedHtml = embedHtml.replace(/\"/g, '"');
|
||||
resolve([embedHtml, data.items[0].snippet.channelId]);
|
||||
ft.log('Video ID: ', videoId);
|
||||
return new Promise((resolve, reject) => {
|
||||
youtubeAPI('videos', {
|
||||
part: 'snippet,player',
|
||||
id: videoId,
|
||||
}, function (data) {
|
||||
let embedHtml = data.items[0].player.embedHtml;
|
||||
embedHtml = embedHtml.replace('src="', 'src="https:');
|
||||
embedHtml = embedHtml.replace('width="480px"', '');
|
||||
embedHtml = embedHtml.replace('height="270px"', '');
|
||||
embedHtml = embedHtml.replace(/\"/g, '"');
|
||||
resolve([embedHtml, data.items[0].snippet.channelId]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -499,68 +544,68 @@ function getChannelAndPlayer(videoId) {
|
|||
* @param {string} video720p - The URL to the 720p video.
|
||||
*/
|
||||
function checkVideoUrls(video480p, video720p) {
|
||||
const currentQuality = $('#currentQuality').html();
|
||||
let buttonEmbed = document.getElementById('qualityEmbed');
|
||||
const currentQuality = $('#currentQuality').html();
|
||||
let buttonEmbed = document.getElementById('qualityEmbed');
|
||||
|
||||
let valid480 = false;
|
||||
let valid480 = false;
|
||||
|
||||
if (typeof(video480p) !== 'undefined') {
|
||||
let get480pUrl = fetch(video480p);
|
||||
get480pUrl.then((status) => {
|
||||
switch (status.status) {
|
||||
case 404:
|
||||
showToast('Found valid URL for 480p, but returned a 404. Video type might be available in the future.');
|
||||
$(document).off('click', '#quality480p');
|
||||
$(document).on('click', '#quality480p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
buttonEmbed.click();
|
||||
return;
|
||||
break;
|
||||
case 403:
|
||||
showToast('This video is unavailable in your country.');
|
||||
$(document).off('click', '#quality480p');
|
||||
$(document).on('click', '#quality480p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
console.log('480p is valid');
|
||||
if (currentQuality === '720p' && typeof(video720p) === 'undefined') {
|
||||
changeQuality(video480p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (typeof (video480p) !== 'undefined') {
|
||||
let get480pUrl = fetch(video480p);
|
||||
get480pUrl.then((status) => {
|
||||
switch (status.status) {
|
||||
case 404:
|
||||
showToast('Found valid URL for 480p, but returned a 404. Video type might be available in the future.');
|
||||
$(document).off('click', '#quality480p');
|
||||
$(document).on('click', '#quality480p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
buttonEmbed.click();
|
||||
return;
|
||||
break;
|
||||
case 403:
|
||||
showToast('This video is unavailable in your country.');
|
||||
$(document).off('click', '#quality480p');
|
||||
$(document).on('click', '#quality480p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
ft.log('480p is valid');
|
||||
if (currentQuality === '720p' && typeof (video720p) === 'undefined') {
|
||||
changeQuality(video480p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof(video720p) !== 'undefined') {
|
||||
let get720pUrl = fetch(video720p);
|
||||
get720pUrl.then((status) => {
|
||||
switch (status.status) {
|
||||
case 404:
|
||||
showToast('Found valid URL for 720p, but returned a 404. Video type might be available in the future.');
|
||||
$(document).off('click', '#quality720p');
|
||||
$(document).on('click', '#quality720p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
if (typeof(valid480) !== 'undefined') {
|
||||
changeQuality(video480p, '480p');
|
||||
}
|
||||
break;
|
||||
case 403:
|
||||
showToast('This video is unavailable in your country.');
|
||||
$(document).off('click', '#quality720p');
|
||||
$(document).on('click', '#quality720p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
console.log('720p is valid');
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (typeof (video720p) !== 'undefined') {
|
||||
let get720pUrl = fetch(video720p);
|
||||
get720pUrl.then((status) => {
|
||||
switch (status.status) {
|
||||
case 404:
|
||||
showToast('Found valid URL for 720p, but returned a 404. Video type might be available in the future.');
|
||||
$(document).off('click', '#quality720p');
|
||||
$(document).on('click', '#quality720p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
if (typeof (valid480) !== 'undefined') {
|
||||
changeQuality(video480p, '480p');
|
||||
}
|
||||
break;
|
||||
case 403:
|
||||
showToast('This video is unavailable in your country.');
|
||||
$(document).off('click', '#quality720p');
|
||||
$(document).on('click', '#quality720p', (event) => {
|
||||
changeQuality('');
|
||||
});
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
ft.log('720p is valid');
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List a YouTube HTTP API resource.
|
||||
*
|
||||
|
@ -9,10 +28,12 @@
|
|||
*/
|
||||
|
||||
function youtubeAPI(resource, params, success) {
|
||||
params.key = apiKey;
|
||||
params.key = settingsView.apiKey;
|
||||
|
||||
let requestUrl = 'https://www.googleapis.com/youtube/v3/' + resource + '?' + $.param(params);
|
||||
|
||||
if (useTor) {
|
||||
tor.request('https://www.googleapis.com/youtube/v3/' + resource + '?' + $.param(params), function(err, res, body) {
|
||||
tor.request(requestUrl, (err, res, body) => {
|
||||
if (!err && res.statusCode == 200) {
|
||||
success(JSON.parse(body));
|
||||
} else {
|
||||
|
@ -20,20 +41,19 @@ function youtubeAPI(resource, params, success) {
|
|||
console.log(err);
|
||||
console.log(res);
|
||||
console.log(body);
|
||||
stopLoadingAnimation();
|
||||
loadingView.seen = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$.getJSON(
|
||||
'https://www.googleapis.com/youtube/v3/' + resource,
|
||||
params,
|
||||
requestUrl,
|
||||
success
|
||||
).fail((xhr, textStatus, error) => {
|
||||
showToast('There was an error calling the YouTube API.');
|
||||
console.log(error);
|
||||
console.log(xhr);
|
||||
console.log(textStatus);
|
||||
stopLoadingAnimation();
|
||||
loadingView.seen = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -50,19 +70,19 @@ function youtubeAPI(resource, params, success) {
|
|||
*/
|
||||
function youtubedlGetInfo(videoId, callback) {
|
||||
|
||||
let url = 'https://youtube.com/watch?v=' + videoId;
|
||||
let options = ['--all-subs', '--write-subs'];
|
||||
let url = 'https://youtube.com/watch?v=' + videoId;
|
||||
let options = ['--all-subs', '--write-subs'];
|
||||
|
||||
ytdl.getInfo(url, options, function(err, info) {
|
||||
if (err) {
|
||||
showToast(err.message);
|
||||
stopLoadingAnimation();
|
||||
loadingView.seen = false;
|
||||
console.log(err);
|
||||
console.log(info);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Success');
|
||||
callback(info);
|
||||
});
|
||||
}
|
||||
ft.log('Success');
|
||||
callback(info);
|
||||
});
|
||||
}
|
|
@ -1,45 +1,62 @@
|
|||
.channelViewBanner{
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
margin-bottom: 30px;
|
||||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.channelViewBanner {
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.channelViewImage{
|
||||
float: left;
|
||||
width: 100px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
.channelViewImage {
|
||||
float: left;
|
||||
width: 100px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
.channelViewTitle{
|
||||
height: 100px;
|
||||
margin-left: 100px;
|
||||
.channelViewTitle {
|
||||
height: 100px;
|
||||
margin-left: 100px;
|
||||
}
|
||||
|
||||
.channelViewName{
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
margin-left: 20px;
|
||||
position: relative;
|
||||
top: 20px;
|
||||
.channelViewName {
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
margin-left: 20px;
|
||||
position: relative;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.channelViewSubs{
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
top: 20px;
|
||||
.channelViewSubs {
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.channelSubButton{
|
||||
float: right;
|
||||
width: 125px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
.channelSubButton {
|
||||
float: right;
|
||||
width: 125px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channelViewDescription{
|
||||
white-space: pre-line;
|
||||
.channelViewDescription {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
::-webkit-scrollbar {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
|
@ -14,54 +31,218 @@
|
|||
background: #262626;
|
||||
}
|
||||
|
||||
body{background-color: #212121;}
|
||||
input[type=text] {color: #EEEEEE;}
|
||||
.sk-cube-grid .sk-cube {background-color: #f44336;}
|
||||
.searchBar ::-webkit-input-placeholder {color: #E0E0E0;}
|
||||
.topNav{background-color: #262626; -webkit-box-shadow: 0px -4px 20px 0px rgba(0,0,0,0.75);}
|
||||
.logo{color: #f44336;}
|
||||
.searchBar input{border-bottom: 1px solid #ddd;}
|
||||
.searchButton{color: #E0E0E0;}
|
||||
.jumpToInput{border-bottom: 1px solid #E0E0E0;}
|
||||
.message{color: #757575;}
|
||||
.videoDelete{color: white;}
|
||||
.channelViewImage{border: 0px solid white;}
|
||||
.channelViewName{color: #EEEEEE;}
|
||||
.channelViewSubs{color: #BDBDBD;}
|
||||
.channelViewDescription{color: #E0E0E0;}
|
||||
.channelSubButton{color: #E0E0E0; background-color: #f44336;}
|
||||
.videoTitle{color: #EEEEEE;}
|
||||
.channelName{color: #E0E0E0;}
|
||||
.videoDescription{color: #E0E0E0;}
|
||||
.statistics{background-color: #424242; color: #EEEEEE;}
|
||||
.views{color: #E0E0E0;}
|
||||
.details{background-color: #424242; color: #EEEEEE;}
|
||||
.playerSubButton{color: #E0E0E0; background-color: #f44336;}
|
||||
.smallButton{color: #E0E0E0; background-color: #757575;}
|
||||
.recommendDate{color: #E0E0E0;}
|
||||
.settingsButton {color: #BDBDBD; background-color: #424242;}
|
||||
.qualityTypes{color: #E0E0E0; background-color: #757575;}
|
||||
.speedTypes{color: #E0E0E0; background-color: #757575;}
|
||||
.unsaved{color: #E0E0E0;}
|
||||
.double-bounce1, .double-bounce2 {background-color: #f44336;}
|
||||
.videoOptions ul {background-color: #262626; color: white;}
|
||||
.videoOptions li:hover {background-color: #262626;}
|
||||
.videoOptions a {color: white;}
|
||||
body {
|
||||
background-color: #212121;
|
||||
}
|
||||
|
||||
#main{color: #EEEEEE;}
|
||||
#main hr{border-bottom: 1px solid #212121;}
|
||||
#subscriptions img{border: 0px solid white;}
|
||||
#sideNav{background-color: #262626; color: #E0E0E0; -webkit-box-shadow: 4px -2px 51px -6px rgba(0,0,0,0.75);}
|
||||
#sideNav hr{background-color: #f44336;}
|
||||
#sideNav li:hover{background-color: #212121;}
|
||||
#channelIcon{border: 0px solid white;}
|
||||
#channelName{color: #E0E0E0;}
|
||||
#publishDate{color: #E0E0E0;}
|
||||
#description{color: #E0E0E0;}
|
||||
#showComments{background-color: #757575; color: #E0E0E0;}
|
||||
#recommendations{color: #EEEEEE;}
|
||||
#videoListContainer{color: #EEEEEE;}
|
||||
#toast{background-color: #616161; color: white;}
|
||||
#confirmFunction{background-color: #616161; color: white;}
|
||||
#getNextPage{background-color: #616161}
|
||||
#comments{background-color: #424242;}
|
||||
input[type=text] {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.searchBar ::-webkit-input-placeholder {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.topNav {
|
||||
background-color: #262626;
|
||||
-webkit-box-shadow: 0px -4px 20px 0px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.logo {
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
.searchBar input {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.jumpToInput {
|
||||
border-bottom: 1px solid #E0E0E0;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.videoDelete {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.channelViewImage {
|
||||
border: 0px solid white;
|
||||
}
|
||||
|
||||
.channelViewName {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
.channelViewSubs {
|
||||
color: #BDBDBD;
|
||||
}
|
||||
|
||||
.channelViewDescription {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.channelSubButton {
|
||||
color: #E0E0E0;
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.videoTitle {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.videoDescription {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.statistics {
|
||||
background-color: #424242;
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
.views {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.details {
|
||||
background-color: #424242;
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
.playerSubButton {
|
||||
color: #E0E0E0;
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.smallButton {
|
||||
color: #E0E0E0;
|
||||
background-color: #757575;
|
||||
}
|
||||
|
||||
.recommendDate {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.settingsButton {
|
||||
color: #BDBDBD;
|
||||
background-color: #424242;
|
||||
}
|
||||
|
||||
.qualityTypes {
|
||||
color: #E0E0E0;
|
||||
background-color: #757575;
|
||||
}
|
||||
|
||||
.speedTypes {
|
||||
color: #E0E0E0;
|
||||
background-color: #757575;
|
||||
}
|
||||
|
||||
.unsaved {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.double-bounce1,
|
||||
.double-bounce2 {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.videoOptions ul {
|
||||
background-color: #262626;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.videoOptions li:hover {
|
||||
background-color: #262626;
|
||||
}
|
||||
|
||||
.videoOptions a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#main {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
#main hr {
|
||||
border-bottom: 1px solid #212121;
|
||||
}
|
||||
|
||||
#subscriptions img {
|
||||
border: 0px solid white;
|
||||
}
|
||||
|
||||
#sideNav {
|
||||
background-color: #262626;
|
||||
color: #E0E0E0;
|
||||
-webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
#sideNav hr {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
#sideNav li:hover {
|
||||
background-color: #212121;
|
||||
}
|
||||
|
||||
#channelIcon {
|
||||
border: 0px solid white;
|
||||
}
|
||||
|
||||
#channelName {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
#publishDate {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
#description {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
#showComments {
|
||||
background-color: #757575;
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
#recommendations {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
#videoListContainer {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
#toast {
|
||||
background-color: #616161;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#confirmFunction {
|
||||
background-color: #616161;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#getNextPage {
|
||||
background-color: #616161
|
||||
}
|
||||
|
||||
#comments {
|
||||
background-color: #424242;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
::-webkit-scrollbar {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
|
@ -14,43 +31,167 @@
|
|||
background: white;
|
||||
}
|
||||
|
||||
body{background-color: #e0e0e0;}
|
||||
.sk-cube-grid .sk-cube {background-color: #f44336;}
|
||||
.searchBar ::-webkit-input-placeholder {color: #ddd;}
|
||||
.topNav{background-color: #f44336; -webkit-box-shadow: 0px -4px 32px 0px rgba(0, 0, 0, 0.75);}
|
||||
.searchBar input{border-bottom: 1px solid #ddd;}
|
||||
.searchButton{color: black;}
|
||||
.jumpToInput{border-bottom: 1px solid #757575;}
|
||||
.message{color: #757575;}
|
||||
.videoDelete{color: black;}
|
||||
.channelViewImage{border: 0px solid #000000;}
|
||||
.channelViewSubs{color: #757575;}
|
||||
.channelSubButton{color: #616161; background-color: #eeeeee;}
|
||||
.videoTitle{color: #000000;}
|
||||
.channelName{color: #424242;}
|
||||
.videoDescription{color: #424242;}
|
||||
.statistics{background-color: white;}
|
||||
.views{color: #424242;}
|
||||
.details{background-color: white;}
|
||||
.playerSubButton{color: #616161; background-color: #eeeeee;}
|
||||
.smallButton{color: #616161; background-color: #eeeeee;}
|
||||
.recommendDate{color: #616161;}
|
||||
.settingsButton {color: #424242; background-color: #BDBDBD;}
|
||||
.qualityTypes{background-color: #eeeeee;}
|
||||
.speedTypes{background-color: #eeeeee;}
|
||||
.unsaved{color: #616161;}
|
||||
.double-bounce1, .double-bounce2 {background-color: #f44336;}
|
||||
.videoOptions ul {background-color: #f5f5f5;}
|
||||
.videoOptions li:hover {background-color: #e0e0e0;}
|
||||
.videoOptions a {color: black;}
|
||||
body {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
#subscriptions img{border: 0px solid #000000;}
|
||||
#sideNav{background-color: white; -webkit-box-shadow: 4px -2px 51px -6px rgba(0,0,0,0.75);}
|
||||
#sideNav hr{background-color: #f44336;}
|
||||
#sideNav li:hover{background-color: #e0e0e0;}
|
||||
#channelIcon{border: 0px solid #000000;}
|
||||
#showComments{background-color: #eeeeee;}
|
||||
#toast{background-color: #212121; color: #f5f5f5;;}
|
||||
#confirmFunction{background-color: #f5f5f5;}
|
||||
#getNextPage{background-color: #f5f5f5;}
|
||||
#comments{background: #eee;}
|
||||
.sk-cube-grid .sk-cube {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.searchBar ::-webkit-input-placeholder {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.topNav {
|
||||
background-color: #f44336;
|
||||
-webkit-box-shadow: 0px -4px 32px 0px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.searchBar input {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.jumpToInput {
|
||||
border-bottom: 1px solid #757575;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.videoDelete {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.channelViewImage {
|
||||
border: 0px solid #000000;
|
||||
}
|
||||
|
||||
.channelViewSubs {
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.channelSubButton {
|
||||
color: #616161;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.videoTitle {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.videoDescription {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.statistics {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.views {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.details {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.playerSubButton {
|
||||
color: #616161;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.smallButton {
|
||||
color: #616161;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.recommendDate {
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
.settingsButton {
|
||||
color: #424242;
|
||||
background-color: #BDBDBD;
|
||||
}
|
||||
|
||||
.qualityTypes {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.speedTypes {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.unsaved {
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
.double-bounce1,
|
||||
.double-bounce2 {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.videoOptions ul {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.videoOptions li:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.videoOptions a {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#subscriptions img {
|
||||
border: 0px solid #000000;
|
||||
}
|
||||
|
||||
#sideNav {
|
||||
background-color: white;
|
||||
-webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
#sideNav hr {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
#sideNav li:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
#channelIcon {
|
||||
border: 0px solid #000000;
|
||||
}
|
||||
|
||||
#showComments {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
#toast {
|
||||
background-color: #212121;
|
||||
color: #f5f5f5;
|
||||
;
|
||||
}
|
||||
|
||||
#confirmFunction {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#getNextPage {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#comments {
|
||||
background: #eee;
|
||||
}
|
||||
|
|
|
@ -1,45 +1,71 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Thanks to @tobiasahlin for the loading animation.
|
||||
* Find it here: http://tobiasahlin.com/spinkit/
|
||||
* Twitter: https://twitter.com/tobiasahlin
|
||||
*/
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
position: relative;
|
||||
margin: 100px auto;
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
position: relative;
|
||||
margin: 100px auto;
|
||||
}
|
||||
|
||||
.double-bounce1, .double-bounce2 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
.double-bounce1,
|
||||
.double-bounce2 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.double-bounce2 {
|
||||
-webkit-animation-delay: -1.0s;
|
||||
animation-delay: -1.0s;
|
||||
-webkit-animation-delay: -1.0s;
|
||||
animation-delay: -1.0s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-bounce {
|
||||
0%, 100% { -webkit-transform: scale(0.0) }
|
||||
50% { -webkit-transform: scale(1.0) }
|
||||
0%,
|
||||
100% {
|
||||
-webkit-transform: scale(0.0)
|
||||
}
|
||||
50% {
|
||||
-webkit-transform: scale(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(0.0);
|
||||
-webkit-transform: scale(0.0);
|
||||
} 50% {
|
||||
transform: scale(1.0);
|
||||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(0.0);
|
||||
-webkit-transform: scale(0.0);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.0);
|
||||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,170 +1,197 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url(Roboto-Regular.ttf);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
input {
|
||||
box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
padding: 7px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
margin-bottom: 10px;
|
||||
font: 16px;
|
||||
height: 45px;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
padding: 7px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
margin-bottom: 10px;
|
||||
font: 16px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #2196F3;
|
||||
color: #2196F3;
|
||||
}
|
||||
|
||||
#progressBar{
|
||||
height: 3px;
|
||||
background-color: #f44336;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.topNav {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.topNav span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: normal;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
#menuButton {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
margin-left: 20px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
margin-left: 20px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
#menuIcon {
|
||||
width: 25px;
|
||||
position: relative;
|
||||
top: 15px;
|
||||
right: 120px;
|
||||
float: right;
|
||||
width: 25px;
|
||||
position: relative;
|
||||
top: 15px;
|
||||
right: 120px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#menuText {
|
||||
width: 100px;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
left: 10px;
|
||||
float: right;
|
||||
width: 100px;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
left: 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.searchBar {
|
||||
position: absolute;
|
||||
right: 135px;
|
||||
top: 0;
|
||||
width: 50%;
|
||||
position: absolute;
|
||||
right: 135px;
|
||||
top: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.searchBar input {
|
||||
width: 90%;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
text-decoration: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#sideNav {
|
||||
height: 100vh;
|
||||
width: 250px;
|
||||
overflow-y: auto;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0px;
|
||||
-webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75);
|
||||
height: 100vh;
|
||||
width: 250px;
|
||||
overflow-y: auto;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0px;
|
||||
-webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
#sideNav hr {
|
||||
width: 95%;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
width: 95%;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#sideNav ul {
|
||||
list-style-type: none;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#sideNav li {
|
||||
padding: 20px;
|
||||
width: 205px;
|
||||
position: relative;
|
||||
right: 50px;
|
||||
cursor: pointer;
|
||||
-webkit-transition: background 0.2s ease-out;
|
||||
-moz-transition: background 0.2s ease-out;
|
||||
-o-transition: background 0.2s ease-out;
|
||||
transition: background 0.2s ease-out;
|
||||
padding: 20px;
|
||||
width: 205px;
|
||||
position: relative;
|
||||
right: 50px;
|
||||
cursor: pointer;
|
||||
-webkit-transition: background 0.2s ease-out;
|
||||
-moz-transition: background 0.2s ease-out;
|
||||
-o-transition: background 0.2s ease-out;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
#sideNav li:hover {
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
#sideNavDisabled {
|
||||
height: 100vh;
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0px;
|
||||
z-index: 1;
|
||||
margin-top: 85px;
|
||||
display: none;
|
||||
height: 100vh;
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0px;
|
||||
z-index: 1;
|
||||
margin-top: 85px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sideNavContainer {
|
||||
margin-top: 85px;
|
||||
margin-top: 85px;
|
||||
}
|
||||
|
||||
#subscriptions img {
|
||||
float: left;
|
||||
width: 40px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
position: relative;
|
||||
top: -12px;
|
||||
margin-right: 5px;
|
||||
float: left;
|
||||
width: 40px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
position: relative;
|
||||
top: -12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#subscriptions .fa-times {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#subscriptions li {
|
||||
font-size: 11px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.message {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.videoDelete {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#loading {
|
||||
|
@ -176,198 +203,200 @@ a {
|
|||
left: 5;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.settingsInput {
|
||||
width: 50%;
|
||||
border-bottom: 1px solid #616161;
|
||||
width: 50%;
|
||||
border-bottom: 1px solid #616161;
|
||||
}
|
||||
|
||||
.settingsButton {
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
-webkit-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
-moz-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
-webkit-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
-moz-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.settingsSubmit {
|
||||
padding: 15px;
|
||||
color: #212121;
|
||||
background-color: #f44336;
|
||||
cursor: pointer;
|
||||
width: 150px;
|
||||
-webkit-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
-moz-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
padding: 15px;
|
||||
color: #212121;
|
||||
background-color: #f44336;
|
||||
cursor: pointer;
|
||||
width: 150px;
|
||||
-webkit-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
-moz-box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 4px 4px 10px 0px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.help {
|
||||
margin: 30px;
|
||||
margin: 30px;
|
||||
}
|
||||
|
||||
.live {
|
||||
font-size: 12px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
width: 57px;
|
||||
position: relative;
|
||||
left: 284px;
|
||||
color: #f44336;
|
||||
bottom: 15px;
|
||||
line-height: 10px;
|
||||
font-size: 12px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
width: 57px;
|
||||
position: relative;
|
||||
left: 284px;
|
||||
color: #f44336;
|
||||
bottom: 15px;
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
#toast{
|
||||
min-width: 400px;
|
||||
height: 50px;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
line-height: 50px;
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
visibility: hidden;
|
||||
-webkit-transition: opacity 0.5s ease-in-out;
|
||||
-moz-transition: opacity 0.5s ease-in-out;
|
||||
-ms-transition: opacity 0.5s ease-in-out;
|
||||
-o-transition: opacity 0.5s ease-in-out;
|
||||
#toast {
|
||||
min-width: 400px;
|
||||
height: 50px;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
line-height: 50px;
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
visibility: hidden;
|
||||
-webkit-transition: opacity 0.5s ease-in-out;
|
||||
-moz-transition: opacity 0.5s ease-in-out;
|
||||
-ms-transition: opacity 0.5s ease-in-out;
|
||||
-o-transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.closeToast{
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
.closeToast {
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#confirmFunction{
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 30%;
|
||||
width: 800px;
|
||||
height: 65px;
|
||||
font-weight: bold;
|
||||
line-height: 65px;
|
||||
visibility: hidden;
|
||||
z-index: 1;
|
||||
-webkit-box-shadow: 5px 5px 15px -5px rgba(0,0,0,0.75);
|
||||
-moz-box-shadow: 5px 5px 15px -5px rgba(0,0,0,0.75);
|
||||
box-shadow: 5px 5px 15px -5px rgba(0,0,0,0.75);
|
||||
#confirmFunction {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 30%;
|
||||
width: 800px;
|
||||
height: 65px;
|
||||
font-weight: bold;
|
||||
line-height: 65px;
|
||||
visibility: hidden;
|
||||
z-index: 1;
|
||||
-webkit-box-shadow: 5px 5px 15px -5px rgba(0, 0, 0, 0.75);
|
||||
-moz-box-shadow: 5px 5px 15px -5px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 5px 5px 15px -5px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
#confirmMessage{
|
||||
margin-left: 15px;
|
||||
#confirmMessage {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.confirmButton{
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
cursor: pointer;
|
||||
.confirmButton {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#confirmYes{
|
||||
right: 90px;
|
||||
color: #2196f3;
|
||||
#confirmYes {
|
||||
right: 90px;
|
||||
color: #2196f3;
|
||||
}
|
||||
|
||||
#confirmNo{
|
||||
right: 20px;
|
||||
#confirmNo {
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
#getNextPage{
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
#getNextPage {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#comments{
|
||||
display:none;
|
||||
padding:1em;
|
||||
margin-bottom: 1em;
|
||||
#comments {
|
||||
display: none;
|
||||
padding: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.saved{color: #FFEB3B;}
|
||||
.saved {
|
||||
color: #FFEB3B;
|
||||
}
|
||||
|
||||
/* Thanks to Guus Lieben for the Material Design Switch */
|
||||
|
||||
.switch-input {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.switch-label {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: 112px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
margin: 16px;
|
||||
padding: 16px 0 16px 44px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: 112px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
margin: 16px;
|
||||
padding: 16px 0 16px 44px;
|
||||
}
|
||||
|
||||
.switch-label:before, .switch-label:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
top: 50%;
|
||||
-ms-transform: translate(0, -50%);
|
||||
-webkit-transform: translate(0, -50%);
|
||||
transform: translate(0, -50%);
|
||||
-webkit-transition: all 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
.switch-label:before,
|
||||
.switch-label:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
top: 50%;
|
||||
-ms-transform: translate(0, -50%);
|
||||
-webkit-transform: translate(0, -50%);
|
||||
transform: translate(0, -50%);
|
||||
-webkit-transition: all 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.switch-label:before {
|
||||
left: 1px;
|
||||
width: 34px;
|
||||
height: 14px;
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 8px;
|
||||
left: 1px;
|
||||
width: 34px;
|
||||
height: 14px;
|
||||
background-color: #9E9E9E;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.switch-label:after {
|
||||
left: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #FAFAFA;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.14), 0 2px 2px 0 rgba(0, 0, 0, 0.098), 0 1px 5px 0 rgba(0, 0, 0, 0.084);
|
||||
left: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #FAFAFA;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.14), 0 2px 2px 0 rgba(0, 0, 0, 0.098), 0 1px 5px 0 rgba(0, 0, 0, 0.084);
|
||||
}
|
||||
|
||||
.switch-label .toggle--on {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.switch-label .toggle--off {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.switch-input:checked+.switch-label:before {
|
||||
background-color: #90CAF9;
|
||||
background-color: #90CAF9;
|
||||
}
|
||||
|
||||
.switch-input:checked+.switch-label:after {
|
||||
background-color: #2196F3;
|
||||
-ms-transform: translate(80%, -50%);
|
||||
-webkit-transform: translate(80%, -50%);
|
||||
transform: translate(80%, -50%);
|
||||
background-color: #2196F3;
|
||||
-ms-transform: translate(80%, -50%);
|
||||
-webkit-transform: translate(80%, -50%);
|
||||
transform: translate(80%, -50%);
|
||||
}
|
||||
|
||||
.switch-input:checked+.switch-label .toggle--on {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.switch-input:checked+.switch-label .toggle--off {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,338 +1,365 @@
|
|||
/*
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 41.25vw;
|
||||
width: 100%;
|
||||
height: 41.25vw;
|
||||
}
|
||||
|
||||
#main {
|
||||
margin-top: 80px;
|
||||
margin-left: 250px;
|
||||
margin-top: 80px;
|
||||
margin-left: 250px;
|
||||
}
|
||||
|
||||
.video {
|
||||
width: 95%;
|
||||
max-width: 1000px;
|
||||
height: 145px;
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
width: 95%;
|
||||
max-width: 1000px;
|
||||
height: auto;
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.videoThumbnail {
|
||||
width: 275px;
|
||||
height: 160px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
width: 275px;
|
||||
height: 160px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoThumbnail img{
|
||||
width: 100%;
|
||||
.videoThumbnail img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.videoThumbnail i {
|
||||
color: white;
|
||||
background-color: black;
|
||||
padding: 6px;
|
||||
opacity: 0.7;
|
||||
position: relative;
|
||||
bottom: 159px;
|
||||
left: 247px;
|
||||
}
|
||||
|
||||
.channelThumbnail {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 60px;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 60px;
|
||||
}
|
||||
|
||||
.channelThumbnail img{
|
||||
width: 100%;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
.channelThumbnail img {
|
||||
width: 100%;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
.videoOptions {
|
||||
float: right;
|
||||
width: 50px;
|
||||
float: right;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.videoOptions i {
|
||||
text-align: right;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoOptions ul {
|
||||
width: 90px;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
right: 90px;
|
||||
bottom: 10px;
|
||||
list-style-type: none;
|
||||
display: none;
|
||||
-webkit-box-shadow: 4px 4px 33px -7px rgba(0,0,0,0.75);
|
||||
cursor: pointer;
|
||||
|
||||
width: 90px;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
right: 90px;
|
||||
bottom: 10px;
|
||||
list-style-type: none;
|
||||
display: none;
|
||||
-webkit-box-shadow: 4px 4px 33px -7px rgba(0, 0, 0, 0.75);
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.videoOptions li {
|
||||
width: 110px;
|
||||
position: relative;
|
||||
right: 40px;
|
||||
padding: 10px;
|
||||
-webkit-transition: background 0.2s ease-out;
|
||||
-moz-transition: background 0.2s ease-out;
|
||||
-o-transition: background 0.2s ease-out;
|
||||
transition: background 0.2s ease-out;
|
||||
width: 110px;
|
||||
position: relative;
|
||||
right: 40px;
|
||||
padding: 10px;
|
||||
-webkit-transition: background 0.2s ease-out;
|
||||
-moz-transition: background 0.2s ease-out;
|
||||
-o-transition: background 0.2s ease-out;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.videoOptions li:hover {
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.videoOptions a {
|
||||
text-decoration: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.videoTitle {
|
||||
font-weight: bold;
|
||||
margin-left: 285px;
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
margin-left: 285px;
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
margin-left: 285px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-left: 285px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoDescription {
|
||||
margin-left: 285px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
margin-left: 285px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.videoDuration {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
position: relative;
|
||||
bottom: 36px;
|
||||
color: white;
|
||||
background-color: black;
|
||||
opacity: 0.7;
|
||||
padding: 2px;
|
||||
font-size: 13px;
|
||||
text-align: right;
|
||||
float: right;
|
||||
position: relative;
|
||||
bottom: 36px;
|
||||
color: white;
|
||||
background-color: black;
|
||||
opacity: 0.7;
|
||||
padding: 2px;
|
||||
font-size: 13px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.videoPlayer {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
max-height: 1100px;
|
||||
}
|
||||
|
||||
.statistics {
|
||||
padding: 20px;
|
||||
padding-bottom: 45px;
|
||||
padding: 20px;
|
||||
padding-bottom: 45px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.views {
|
||||
margin-top: -10px;
|
||||
float: left;
|
||||
margin-top: -10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.details {
|
||||
padding: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.likeContainer {
|
||||
width: 300px;
|
||||
float: right;
|
||||
width: 300px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.likes {
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dislikes {
|
||||
float: right;
|
||||
font-size: 12px;
|
||||
float: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dislikeBar {
|
||||
background-color: #9E9E9E;
|
||||
width: 300px;
|
||||
height: 5px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
background-color: #9E9E9E;
|
||||
width: 300px;
|
||||
height: 5px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
.likeBar {
|
||||
height: 5px;
|
||||
background-color: #2196F3;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
height: 5px;
|
||||
background-color: #2196F3;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
#channelIcon {
|
||||
float: left;
|
||||
width: 80px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
width: 80px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#comments .line {
|
||||
clear: both;
|
||||
height: 1px;
|
||||
background: #d8d8d8;
|
||||
clear: both;
|
||||
height: 1px;
|
||||
background: #d8d8d8;
|
||||
}
|
||||
|
||||
.userIcon {
|
||||
float: left;
|
||||
width: 48px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
width: 48px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#channelName {
|
||||
font-weight: bold;
|
||||
margin-left: 95px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
margin-left: 95px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#publishDate {
|
||||
margin-left: 95px;
|
||||
font-size: 13px;
|
||||
margin-top: -10px;
|
||||
margin-left: 95px;
|
||||
font-size: 13px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
#description {
|
||||
white-space: pre-line;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
white-space: pre-line;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.playerSubButton {
|
||||
float: right;
|
||||
width: 125px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
margin-top: -65px;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
width: 125px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
margin-top: -65px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.smallButton {
|
||||
float: right;
|
||||
height: 30px;
|
||||
font-size: 10px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
height: 30px;
|
||||
font-size: 10px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoQuality {
|
||||
width: 42px;
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.videoQuality:hover .qualityTypes {
|
||||
visibility: visible;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.qualityTypes {
|
||||
visibility: hidden;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
bottom: 10px;
|
||||
right: 15px;
|
||||
visibility: hidden;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
bottom: 10px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.qualityTypes ul {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
right: 24px;
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
right: 24px;
|
||||
}
|
||||
|
||||
.qualityTypes ul li {
|
||||
width: 72px;
|
||||
position: relative;
|
||||
right: 15px;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.videoSpeed {
|
||||
width: 42px;
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.videoSpeed:hover .speedTypes {
|
||||
visibility: visible;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.speedTypes {
|
||||
visibility: hidden;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
bottom: 10px;
|
||||
right: 15px;
|
||||
visibility: hidden;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
bottom: 10px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.speedTypes ul {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
right: 24px;
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
right: 24px;
|
||||
}
|
||||
|
||||
.speedTypes ul li {
|
||||
width: 72px;
|
||||
position: relative;
|
||||
right: 15px;
|
||||
width: 72px;
|
||||
position: relative;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
#showComments {
|
||||
text-align: center;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#recommendations {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recommendVideo {
|
||||
cursor: pointer;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.recommendThumbnail {
|
||||
width: 250px;
|
||||
height: 145px;
|
||||
float: left;
|
||||
width: 250px;
|
||||
height: 145px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.recommendThumbnail img{
|
||||
width: 100%;
|
||||
.recommendThumbnail img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recommendTitle {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-left: 260px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-left: 260px;
|
||||
}
|
||||
|
||||
.recommendChannel {
|
||||
margin-left: 260px;
|
||||
font-size: 14px;
|
||||
margin-top: -10px;
|
||||
margin-left: 260px;
|
||||
font-size: 14px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.recommendDate {
|
||||
margin-left: 260px;
|
||||
font-size: 11px;
|
||||
margin-left: 260px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
.videoListContainer{width: 100%;}
|
||||
.videoListContainer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.videoListContainer hr{
|
||||
.videoListContainer hr {
|
||||
width: 95%;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
<div class='center'>
|
||||
<!--
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube is free software: you can redistribute it and/or modify
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<div class='center' v-if="seen">
|
||||
<img src='icons/logoColor.png' width='500px;'/>
|
||||
<h1>{{versionNumber}} Beta</h1>
|
||||
<h1>{{ versionNumber }} Beta</h1>
|
||||
<h3><i class='fas fa-envelope'></i> FreeTubeApp@protonmail.com</h3>
|
||||
<h2>This software is FOSS and released under the <a href='https://www.gnu.org/licenses/quick-guide-gplv3.html'>GNU Public License v3+</a>.</h2>
|
||||
<p>Found a bug? Want to suggest a feature? Want to help out? Check out our <a href='https://github.com/FreeTubeApp/FreeTube'>GitHub</a> page. Pull requests are welcome.</p>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<div class='video'>
|
||||
<div class='channelThumbnail'>
|
||||
<img onclick='goToChannel("{{channelId}}")' src={{channelThumbnail}} />
|
||||
</div>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='videoTitle'>{{channelName}}</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='channelName'>{{subscriberCount}} subscribers - {{videoCount}} videos</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='videoDescription'>{{channelDescription}}</p>
|
||||
</div>
|
||||
<hr />
|
|
@ -1,21 +1,37 @@
|
|||
<img class='channelViewBanner' src='{{channelBanner}}' />
|
||||
<br />
|
||||
<div class='channelViewTitle'>
|
||||
<img class='channelViewImage' src='{{channelImage}}' />
|
||||
<span class='channelViewName'>{{channelName}}</span>
|
||||
<!--
|
||||
|
||||
This file is part of FreeTube.
|
||||
FreeTube is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
(at your option) any later version.
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
|
||||
FreeTube is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
GNU General Public License for more details.
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<div v-if='seen'>
|
||||
<img class='channelViewBanner' :src='banner' />
|
||||
<br />
|
||||
<span class='channelViewSubs'>{{subCount}} Subscribers</span>
|
||||
<div id='subscribeButton' class='channelSubButton' onclick='toggleSubscription("{{channelId}}");'>
|
||||
{{subButtonText}}
|
||||
<div class='channelViewTitle'>
|
||||
<img class='channelViewImage' :src='icon' />
|
||||
<span class='channelViewName'>{{name}}</span>
|
||||
<br />
|
||||
<span class='channelViewSubs'>{{subCount}} Subscribers</span>
|
||||
<div id='subscribeButton' class='channelSubButton' v-on:click='subscription(id);'>
|
||||
{{subButtonText}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<hr />
|
||||
<div class='channelViewDescription'>
|
||||
{{{channelDescription}}}
|
||||
</div>
|
||||
<br />
|
||||
<hr />
|
||||
<div id='videoListContainer'>
|
||||
<h2>Latest Uploads</h2>
|
||||
<br />
|
||||
<hr />
|
||||
<div class='channelViewDescription'>
|
||||
<span v-html='description'></span>
|
||||
</div>
|
||||
<br />
|
||||
<hr />
|
||||
</div>
|
||||
|
|
|
@ -1,14 +1,31 @@
|
|||
<!--
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
{{#comments}}
|
||||
<div class="user-comment">
|
||||
<div onclick='goToChannel("{{authorChannelId.value}}")' style="float:left;margin-right: 1em; width:48px">
|
||||
<img class="userIcon" src="{{authorProfileImageUrl}}" alt="" class="user-icon" />
|
||||
<div class="user-comment">
|
||||
<div onclick='goToChannel("{{authorChannelId.value}}")' style="float:left;margin-right: 1em; width:48px">
|
||||
<img class="userIcon" src="{{authorProfileImageUrl}}" alt="" class="user-icon" />
|
||||
</div>
|
||||
<div class="comment-data">
|
||||
<p onclick='goToChannel("{{authorChannelId.value}}")' style='cursor: pointer; margin-left: 65px; font-weight: bold;'>{{authorDisplayName}}</p>
|
||||
<p style="margin-bottom: 0; margin-left: 65px; word-break: break-word;">{{textOriginal}}</p>
|
||||
<p style="font-size:80%; padding: 0; margin-left: 65px;">{{publishedAt}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment-data">
|
||||
<p onclick='goToChannel("{{authorChannelId.value}}")' style='cursor: pointer; margin-left: 65px; font-weight: bold;'>{{authorDisplayName}}</p>
|
||||
<p style="margin-bottom: 0; margin-left: 65px; word-break: break-word;">{{textOriginal}}</p>
|
||||
<p style="font-size:80%; padding: 0; margin-left: 65px;">{{publishedAt}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="line"></div>
|
||||
<br/>
|
||||
<div class="line"></div>
|
||||
{{/comments}}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<div v-if="seen">
|
||||
<h2 style='margin-left: 15px;'>{{ title }}</h2>
|
||||
<hr />
|
||||
</div>
|
|
@ -1,15 +1,35 @@
|
|||
<!--
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<title>Freetube Mini-Player</title>
|
||||
<style>
|
||||
body{
|
||||
background-color: #424242;
|
||||
body {
|
||||
background-color: #424242;
|
||||
}
|
||||
.videoPlayer{
|
||||
width: 100%;
|
||||
|
||||
.videoPlayer {
|
||||
width: 100%;
|
||||
}
|
||||
iframe{
|
||||
width: 100%;
|
||||
height: 54.9vw;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 54.9vw;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="../js/events.js"></script>
|
||||
<script>
|
||||
|
@ -18,40 +38,41 @@ let startTime = '{{startTime}}';
|
|||
let mouseTimeout;
|
||||
|
||||
$('.videoPlayer').ready(() => {
|
||||
$('.videoPlayer').get(0).currentTime = startTime;
|
||||
$('.videoPlayer').get(0).currentTime = startTime;
|
||||
});
|
||||
|
||||
/**
|
||||
* Hide the mouse cursor after ~3 seconds. Used to hide the video when the user
|
||||
* hovers the mouse over the video player.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function hideMouseTimeout(){
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
mouseTimeout = window.setTimeout(function(){
|
||||
$('.videoPlayer')[0].style.cursor = 'none';
|
||||
}, 3150);
|
||||
* Hide the mouse cursor after ~3 seconds. Used to hide the video when the user
|
||||
* hovers the mouse over the video player.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function hideMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
mouseTimeout = window.setTimeout(function() {
|
||||
$('.videoPlayer')[0].style.cursor = 'none';
|
||||
}, 3150);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the timeout for the mouse cursor as a fallback.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function removeMouseTimeout(){
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
* Remove the timeout for the mouse cursor as a fallback.
|
||||
*
|
||||
* @return {Void}
|
||||
*/
|
||||
function removeMouseTimeout() {
|
||||
$('.videoPlayer')[0].style.cursor = 'default';
|
||||
clearTimeout(mouseTimeout);
|
||||
}
|
||||
|
||||
let playPauseVideo = function(event) {
|
||||
let el = event.currentTarget;
|
||||
el.paused ? el.play() : el.pause();
|
||||
let el = event.currentTarget;
|
||||
el.paused ? el.play() : el.pause();
|
||||
}
|
||||
|
||||
$(document).on('click', '.videoPlayer', playPauseVideo);
|
||||
$(document).on('mouseover', '.videoPlayer', hideMouseTimeout);
|
||||
$(document).on('mouseleave', '.videoPlayer', removeMouseTimeout);
|
||||
|
||||
</script>
|
||||
{{{videoHtml}}}
|
||||
|
|
|
@ -1,82 +1,117 @@
|
|||
<video class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" src="{{defaultUrl}}" poster="{{videoThumbnail}}" autoplay>
|
||||
{{{subtitleHtml}}}
|
||||
</video>
|
||||
<div class='statistics'>
|
||||
<div class='smallButton' onclick="openMiniPlayer('{{videoThumbnail}}')">
|
||||
MINI PLAYER <i class="fas fa-external-link-alt"></i>
|
||||
<!--
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
along with FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
-->
|
||||
|
||||
<div v-if='seen'>
|
||||
<div v-if='playerSeen'>
|
||||
<video class="videoPlayer" type="application/x-mpegURL" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" :src='videoUrl' :poster="videoThumbnail" v-html="subtitleHtml" autoplay>
|
||||
</video>
|
||||
</div>
|
||||
<div class='smallButton videoQuality'>
|
||||
<span id='currentQuality'>{{videoQuality}}</span> <i class="fas fa-angle-down"></i>
|
||||
<div class='qualityTypes'>
|
||||
<ul>
|
||||
<li id='quality480p' onclick='changeQuality("{{video480p}}", "480p")'>480p</li>
|
||||
<li id='quality720p' onclick='changeQuality("{{video720p}}", "720p")'>720p</li>
|
||||
<li id='qualityEmbed' onclick='changeQuality("{{embedPlayer}}", "EMBED", true)'>EMBED</li>
|
||||
</ul>
|
||||
<div v-else>
|
||||
<span v-html="embededHtml"></span>
|
||||
</div>
|
||||
<div class='statistics'>
|
||||
<div onclick='openMiniPlayer()' class='smallButton' >
|
||||
MINI PLAYER <i class="fas fa-external-link-alt"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class='smallButton videoSpeed'>
|
||||
<span id='currentSpeed'>1</span>x <i class="fas fa-angle-down"></i>
|
||||
<div class='speedTypes'>
|
||||
<ul>
|
||||
<li onclick='changeVideoSpeed(0.25)'>0.25x</li>
|
||||
<li onclick='changeVideoSpeed(0.5)'>0.5x</li>
|
||||
<li onclick='changeVideoSpeed(0.75)'>0.75x</li>
|
||||
<li onclick='changeVideoSpeed(1)'>1x</li>
|
||||
<li onclick='changeVideoSpeed(1.25)'>1.25x</li>
|
||||
<li onclick='changeVideoSpeed(1.5)'>1.5x</li>
|
||||
<li onclick='changeVideoSpeed(1.75)'>1.75x</li>
|
||||
<li onclick='changeVideoSpeed(2)'>2x</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div onclick='toggleSavedVideo("{{videoId}}")' class='smallButton'>
|
||||
<i id='saveIcon' style='color: {{savedIconColor}};' class="{{savedIconClass}} fa-star"></i> <span id='savedText'>{{savedText}}</span>
|
||||
</div>
|
||||
<div class='smallButton' onclick='copyLink("youtube", "{{videoId}}")'>
|
||||
COPY YOUTUBE LINK
|
||||
</div>
|
||||
<a href='https://youtube.com/watch?v={{videoId}}'>
|
||||
<div class='smallButton'>
|
||||
OPEN IN YOUTUBE
|
||||
</div>
|
||||
</a>
|
||||
<div class='smallButton' onclick='copyLink("hooktube", "{{videoId}}")'>
|
||||
COPY HOOKTUBE LINK
|
||||
</div>
|
||||
<a href='https://hooktube.com/watch?v={{videoId}}'>
|
||||
<div class='smallButton'>
|
||||
OPEN IN HOOKTUBE
|
||||
</div>
|
||||
</a>
|
||||
<br />
|
||||
<p class='title'>{{videoTitle}}</p>
|
||||
<p class='views'>{{videoViews}} views</p>
|
||||
<div class='likeContainer'>
|
||||
<div class='dislikeBar'>
|
||||
<div class='likeBar' style='width: {{likePercentage}}%;'>
|
||||
<div class='smallButton videoQuality'>
|
||||
<span id='currentQuality'>{{currentQuality}}</span> <i class="fas fa-angle-down"></i>
|
||||
<div class='qualityTypes'>
|
||||
<ul>
|
||||
<li id='quality480p' v-on:click='quality(video480p, "480p")'>480p</li>
|
||||
<li id='quality720p' v-on:click='quality(video720p, "720p")'>720p</li>
|
||||
<li id='qualityEmbed' v-on:click='embededPlayer()'>EMBED</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<span class='likes'><i class="fas fa-thumbs-up"></i> {{videoLikes}}</span><span class='dislikes'><i class="fas fa-thumbs-down"></i> {{videoDislikes}}</span>
|
||||
<div class='smallButton videoSpeed'>
|
||||
<span id='currentSpeed'>{{currentSpeed}}</span>x <i class="fas fa-angle-down"></i>
|
||||
<div class='speedTypes'>
|
||||
<ul>
|
||||
<li onclick='changeVideoSpeed(0.25)'>0.25x</li>
|
||||
<li onclick='changeVideoSpeed(0.5)'>0.5x</li>
|
||||
<li onclick='changeVideoSpeed(0.75)'>0.75x</li>
|
||||
<li onclick='changeVideoSpeed(1)'>1x</li>
|
||||
<li onclick='changeVideoSpeed(1.25)'>1.25x</li>
|
||||
<li onclick='changeVideoSpeed(1.5)'>1.5x</li>
|
||||
<li onclick='changeVideoSpeed(1.75)'>1.75x</li>
|
||||
<li onclick='changeVideoSpeed(2)'>2x</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class='smallButton' v-on:click='save(videoId)'>
|
||||
<i id='saveIcon' :class='savedIconType' class="fa-star"></i> <span id='savedText'>{{savedText}}</span>
|
||||
</div>
|
||||
<div class='smallButton' v-on:click='copy("youtube.com", videoId)'>
|
||||
COPY YOUTUBE LINK
|
||||
</div>
|
||||
<a :href='"https://youtube.com/watch?v=" + videoId'>
|
||||
<div class='smallButton'>
|
||||
OPEN IN YOUTUBE
|
||||
</div>
|
||||
</a>
|
||||
<div class='smallButton' v-on:click='copy("invidio.us", videoId)'>
|
||||
COPY INVIDIOUS LINK
|
||||
</div>
|
||||
<a :href='"https://invidio.us/watch?v=" + videoId'>
|
||||
<div class='smallButton'>
|
||||
OPEN IN INVIDIOUS
|
||||
</div>
|
||||
</a>
|
||||
<br />
|
||||
<p class='title'>{{videoTitle}}</p>
|
||||
<p class='views'>{{videoViews}} views</p>
|
||||
<div class='likeContainer'>
|
||||
<div class='dislikeBar'>
|
||||
<div class='likeBar' :style='{width: likePercentage + "%"}'>
|
||||
</div>
|
||||
</div>
|
||||
<span class='likes'><i class="fas fa-thumbs-up"></i> {{videoLikes}}</span><span class='dislikes'><i class="fas fa-thumbs-down"></i> {{videoDislikes}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="details">
|
||||
<img id='channelIcon' v-on:click='channel(channelId)' :src="channelIcon" />
|
||||
<p id='channelName' v-on:click='channel(channelId)'>{{channelName}}</p>
|
||||
<p id='publishDate'>Published on {{publishedDate}}</p>
|
||||
<div id='subscribeButton' class='playerSubButton' v-on:click='subscription(channelId)'>{{subscribedText}}</div>
|
||||
<br /><br />
|
||||
<div id='description'>
|
||||
<span v-html="description"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id='showComments'>
|
||||
Show Comments <i class="far fa-comments"></i> (Max of 100)
|
||||
</div>
|
||||
|
||||
<div id='comments' :data-video-id="videoId">
|
||||
</div>
|
||||
|
||||
<div id='recommendations'>
|
||||
<strong>Recommendations</strong>
|
||||
<div v-for='video in recommendedVideoList'>
|
||||
<div class='recommendVideo' v-on:click='play(video.id)'>
|
||||
<div class='recommendThumbnail'>
|
||||
<img :src='video.thumbnail'></img>
|
||||
<p v-on:click='play(video.id)' class='videoDuration'>{{video.duration}}</p>
|
||||
</div>
|
||||
<p class='recommendTitle'>{{video.title}}</p>
|
||||
<p class='recommendChannel'>{{video.channelName}}</p>
|
||||
<p class='recommendDate'>{{video.publishedDate}}</p>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="details">
|
||||
<img id='channelIcon' onclick='goToChannel("{{channelId}}")' src="{{channelIcon}}" />
|
||||
<p id='channelName' onclick='goToChannel("{{channelId}}")'>{{channelName}}</p>
|
||||
<p id='publishDate'>Published on {{publishedDate}}</p>
|
||||
<div id='subscribeButton' class='playerSubButton' onclick='toggleSubscription("{{channelId}}")'>{{isSubscribed}}</div>
|
||||
<br /><br />
|
||||
<div id='description'>
|
||||
{{{description}}}
|
||||
</div>
|
||||
</div>
|
||||
<div id='showComments'>
|
||||
Show Comments <i class="far fa-comments"></i> (Max of 100)
|
||||
</div>
|
||||
|
||||
<div id='comments' data-video-id="{{videoId}}">
|
||||
</div>
|
||||
|
||||
<div id='recommendations'>
|
||||
<strong>Recommendations</strong>
|
||||
</div>
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="style/main.css">
|
||||
<link rel="stylesheet" href="style/lightTheme.css">
|
||||
<link rel="stylesheet" href="style/player.css">
|
||||
<link rel="stylesheet" href="style/videoList.css">
|
||||
<link rel="stylesheet" href="style/channel.css">
|
||||
<link rel="stylesheet" href="style/loading.css">
|
||||
<link rel="stylesheet" href="style/fa-solid.min.css">
|
||||
<link rel="stylesheet" href="style/fontawesome-all.min.css">
|
||||
<script src="js/youtubeApi.js"></script>
|
||||
<script src="js/settings.js"></script>
|
||||
<script src="js/layout.js"></script>
|
||||
<script src="js/videos.js"></script>
|
||||
<script src="js/player.js"></script>
|
||||
<script src="js/subscriptions.js"></script>
|
||||
<script src="js/channels.js"></script>
|
||||
<script src="js/savedVideos.js"></script>
|
||||
<script src="js/history.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script>
|
||||
showVideoRecommendations("{{videoId}}");
|
||||
|
||||
const checkIfSubscribed = isSubscribed('{{channelId}}');
|
||||
|
||||
checkIfSubscribed.then((results) => {
|
||||
|
||||
const subscribeButton = document.getElementById('subscribeButton');
|
||||
console.log(results);
|
||||
console.log(subscribeButton);
|
||||
|
||||
if (results === false) {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'UNSUBSCRIBE';
|
||||
}
|
||||
} else {
|
||||
if (subscribeButton != null) {
|
||||
subscribeButton.innerHTML = 'SUBSCRIBE';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<title>Freetube Player</title>
|
||||
<style>
|
||||
#main{
|
||||
width: 100%;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
position: relative;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
#loading{
|
||||
width: 100%;
|
||||
height: 0%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='loading'>
|
||||
<div class="spinner">
|
||||
<div class="double-bounce1"></div>
|
||||
<div class="double-bounce2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id='confirmFunction'>
|
||||
<span id='confirmMessage'>Would you like to perform the function?</span>
|
||||
<div class='confirmButton' id='confirmYes'>Yes</div>
|
||||
<div class='confirmButton' id='confirmNo'>No</div>
|
||||
</div>
|
||||
<div id='toast'>
|
||||
<span id='toastMessage'></span>
|
||||
<i onclick='hideToast()' class="closeToast fas fa-times"></i>
|
||||
</div>
|
||||
<div class="topNav">
|
||||
<i onclick='toggleSideNavigation()' class="fas fa-bars" id='menuButton' style='display: none;'></i>
|
||||
<div class="searchBar">
|
||||
<input id='search' class="search" type="text" placeholder="Search / Go to URL">
|
||||
<i onclick='parseSearchText()' class="fas fa-search searchButton" style='margin-right: -10px; cursor: pointer'></i>
|
||||
</div>
|
||||
<img src='icons/iconBlack.png' id='menuIcon'/>
|
||||
|
||||
<img src='icons/textBlack.png' id='menuText'/>
|
||||
</div>
|
||||
<div id='main'>
|
|
@ -1,10 +1,27 @@
|
|||
<!--
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<div class='video'>
|
||||
<div class='videoThumbnail'>
|
||||
<img onclick='console.log("Not Implemented")' src={{playlistThumbnail}} />
|
||||
</div>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='videoTitle'>{{playlistTitle}}</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='channelName'>{{channelName}} - {{publishedDate}}</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='videoDescription'>{{playlistDescription}}</p>
|
||||
<p>VIEW FULL PLAYLIST ({{videoCount}} videos)</p>
|
||||
<div class='videoThumbnail'>
|
||||
<img onclick='console.log("Not Implemented")' src= {{playlistThumbnail}} />
|
||||
</div>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='videoTitle'>{{playlistTitle}}</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='channelName'>{{channelName}} - {{publishedDate}}</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='videoDescription'>{{playlistDescription}}</p>
|
||||
<p>VIEW FULL PLAYLIST ({{videoCount}} videos)</p>
|
||||
</div>
|
||||
<hr />
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<div v-if='seen'>
|
||||
<span id='progressBar' :style='{ width: progressWidth + "%" }'></span>
|
||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||
<div class='recommendVideo' onclick='playVideo("{{videoId}}")'>
|
||||
<div class='recommendThumbnail'>
|
||||
<img src='{{videoThumbnail}}'></img>
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoDuration'>{{videoDuration}}</p>
|
||||
</div>
|
||||
<p class='recommendTitle'>{{videoTitle}}</p>
|
||||
<p class='recommendChannel'>{{channelName}}</p>
|
||||
<p class='recommendDate'>{{publishedDate}}</p>
|
||||
</div>
|
||||
<hr />
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,35 +1,54 @@
|
|||
<h1 class="center">Settings</h1>
|
||||
<div class='center'>
|
||||
<input type='text' name='api-key' id='api-key' class='settingsInput' value='{{key}}' placeholder='API Key' />
|
||||
<br />
|
||||
<label for='api-key'>Set API Key: Leave blank to use default</label>
|
||||
<br />
|
||||
<input type="checkbox" id="themeSwitch" name="set-name" class="switch-input" onchange='toggleTheme(this)' {{isChecked}}>
|
||||
<label for="themeSwitch" class="switch-label">Use Dark Theme</label>
|
||||
<input type="checkbox" id="torSwitch" name="set-name" class="switch-input" {{isChecked}}>
|
||||
<label for="torSwitch" class="switch-label">Use Tor for API calls</label>
|
||||
</div>
|
||||
<div class='center'>
|
||||
<div onclick='importSubscriptions()' class='settingsButton'>
|
||||
IMPORT SUBSCRIPTIONS
|
||||
<!--
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<div v-if="seen">
|
||||
<h1 class="center">Settings</h1>
|
||||
<div class='center'>
|
||||
<input type='text' name='api-key' id='api-key' class='settingsInput' :value='apiKey' placeholder='API Key' />
|
||||
<br />
|
||||
<label for='api-key'>Set API Key: Leave blank to use default</label>
|
||||
<br />
|
||||
<input type="checkbox" id="themeSwitch" name="set-name" class="switch-input" onchange='toggleTheme(this)' :checked='useTheme'>
|
||||
<label for="themeSwitch" class="switch-label">Use Dark Theme</label>
|
||||
<input type="checkbox" id="torSwitch" name="set-name" class="switch-input" :checked='useTor'>
|
||||
<label for="torSwitch" class="switch-label">Use Tor for API calls</label>
|
||||
</div>
|
||||
<div onclick='exportSubscriptions();' class='settingsButton'>
|
||||
EXPORT SUBSCRIPTIONS
|
||||
<div class='center'>
|
||||
<div onclick='importSubscriptions()' class='settingsButton'>
|
||||
IMPORT SUBSCRIPTIONS
|
||||
</div>
|
||||
<div onclick='exportSubscriptions();' class='settingsButton'>
|
||||
EXPORT SUBSCRIPTIONS
|
||||
</div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div class='center'>
|
||||
<div onclick='confirmFunction("Are you sure you want to delete your history?", clearFile, "history")' class='settingsButton'>
|
||||
CLEAR HISTORY
|
||||
</div>
|
||||
<div onclick='confirmFunction("Are you sure you want to remove all saved videos?", clearFile, "saved")' class='settingsButton'>
|
||||
CLEAR SAVED VIDEOS
|
||||
</div>
|
||||
<div onclick='confirmFunction("Are you sure you want to remove all subscriptions?", clearFile, "subscriptions")' class='settingsButton'>
|
||||
CLEAR SUBSCRIPTIONS
|
||||
</div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div onclick='updateSettings()' class='center settingsSubmit'>
|
||||
SAVE SETTINGS
|
||||
</div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div class='center'>
|
||||
<div onclick='confirmFunction("Are you sure you want to delete your history?", clearFile, "history")' class='settingsButton'>
|
||||
CLEAR HISTORY
|
||||
</div>
|
||||
<div onclick='confirmFunction("Are you sure you want to remove all saved videos?", clearFile, "saved")' class='settingsButton'>
|
||||
CLEAR SAVED VIDEOS
|
||||
</div>
|
||||
<div onclick='confirmFunction("Are you sure you want to remove all subscriptions?", clearFile, "subscriptions")' class='settingsButton'>
|
||||
CLEAR SUBSCRIPTIONS
|
||||
</div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div onclick='updateSettings()' class='center settingsSubmit'>
|
||||
SAVE SETTINGS
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,20 @@
|
|||
<!--
|
||||
This file is part of FreeTube.
|
||||
|
||||
FreeTube 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.
|
||||
|
||||
FreeTube 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 FreeTube. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<li onclick='goToChannel("{{channelId}}")'>
|
||||
<img src='{{channelIcon}}' />
|
||||
{{channelName}}
|
||||
<i class='fas fa-times' onclick='toggleSubscription("{{channelId}}")'></i>
|
||||
<img src='{{channelIcon}}' /> {{channelName}}
|
||||
<i class='fas fa-times' onclick='toggleSubscription("{{channelId}}")'></i>
|
||||
</li>
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<video class="videoPlayer" onmousemove="hideMouseTimeout()" onmouseleave="removeMouseTimeout()" controls="" poster="{{videoThumbnail}}" autoplay>
|
||||
<source src="{{videoUrl}}" type="video/mp4">
|
||||
{{subtitleElements}}
|
||||
</video>
|
|
@ -1,21 +0,0 @@
|
|||
<div class='video'>
|
||||
<div class='videoOptions'>
|
||||
<i class="fas fa-ellipsis-v" onclick='showVideoOptions(this)'></i>
|
||||
<ul>
|
||||
<li onclick='playVideo("{{videoId}}", "", true); showVideoOptions(this.parentNode.previousSibling);'>Open (New Window)</li>
|
||||
<li onclick='addSavedVideo("{{videoId}}"); showVideoOptions(this.parentNode.previousSibling);'>Save Video</li>
|
||||
<a href='https://youtube.com/watch?v={{videoId}}' onclick='showVideoOptions(this.parentNode.previousSibling);'><li>Open in YouTube</li></a>
|
||||
<a href='https://hooktube.com/watch?v={{videoId}}' onclick='showVideoOptions(this.parentNode.previousSibling);'><li>Open in HookTube</li></a>
|
||||
{{{deleteHtml}}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class='videoThumbnail'>
|
||||
<img onclick='playVideo("{{videoId}}", "{{videoThumbnail}}")' src={{videoThumbnail}} />
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoDuration'>{{videoDuration}}</p>
|
||||
</div>
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoTitle'>{{videoTitle}}</p>
|
||||
<p onclick='goToChannel("{{channelId}}")' class='channelName'>{{channelName}} - {{publishedDate}}</p>
|
||||
<p onclick='playVideo("{{videoId}}")' class='videoDescription'>{{videoDescription}}</p>
|
||||
<p class='live'>{{liveText}}</p>
|
||||
</div>
|
||||
<hr />
|
|
@ -0,0 +1,50 @@
|
|||
<div>
|
||||
<div v-if='seen'>
|
||||
<div v-for="video in videoList">
|
||||
<div v-if='video.isVideo'>
|
||||
<div class='video'>
|
||||
<div class='videoOptions'>
|
||||
<i class="fas fa-ellipsis-v" onclick='showVideoOptions(this)'></i>
|
||||
<ul>
|
||||
<a :href='video.youtubeUrl' onclick='showVideoOptions(this.parentNode.previousSibling);'>
|
||||
<li>Open in YouTube</li>
|
||||
</a>
|
||||
<li v-on:click='copy("youtube.com", video.id)' onclick='showVideoOptions(this.parentNode.previousSibling);'>Copy YouTube Link</li>
|
||||
<a :href='video.invidiousUrl' onclick='showVideoOptions(this.parentNode.previousSibling);'>
|
||||
<li>Open in Invidious</li>
|
||||
</a>
|
||||
<li v-on:click='copy("invidio.us", video.id)' onclick='showVideoOptions(this.parentNode.previousSibling);'>Copy Invidious Link</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='videoThumbnail'>
|
||||
<img v-on:click='play(video.id)' :src='video.thumbnail' />
|
||||
<p v-on:click='play(video.id)' class='videoDuration'>{{video.duration}}</p>
|
||||
<i class="fas fa-history" v-on:click='toggleSave(video.id)'></i>
|
||||
</div>
|
||||
<p v-on:click='play(video.id)' class='videoTitle'>{{video.title}}</p>
|
||||
<p v-on:click='channel(video.channelId)' class='channelName'>{{video.channelName}} - {{video.publishedDate}}</p>
|
||||
<p v-on:click='play(video.id)' class='videoDescription'>{{video.description}}</p>
|
||||
<p v-on:click='play(video.id)' class='live'>{{video.liveText}}</p>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
<!-- Channel View -->
|
||||
<div v-else>
|
||||
<div class='video'>
|
||||
<div class='channelThumbnail'>
|
||||
<img v-on:click='channel(video.channelId)' :src='video.thumbnail' />
|
||||
</div>
|
||||
<p v-on:click='channel(video.channelId)' class='videoTitle'>{{video.channelName}}</p>
|
||||
<p v-on:click='channel(video.channelId)' class='channelName'>{{video.subscriberCount}} subscribers - {{video.videoCount}} videos</p>
|
||||
<p v-on:click='channel(video.channelId)' class='videoDescription'>{{video.channelDescription}}</p>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if='isSearch'>
|
||||
<div v-on:click='nextPage' id='getNextPage'>
|
||||
<i class="fas fa-search"></i> Fetch more results...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue