├── .gitignore ├── .gitmodules ├── .idea ├── .name ├── codeStyleSettings.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dataSources.ids ├── dataSources.xml ├── dictionaries │ └── ivailo.xml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── other.xml ├── scopes │ └── scope_settings.xml ├── sqldialects.xml ├── testrunner.xml ├── uiDesigner.xml └── vcs.xml ├── .travis.yml ├── .vscode └── settings.json ├── LICENSE ├── Pipfile ├── README.md ├── Vagrantfile ├── WhatManager2.iml ├── WhatManager2 ├── __init__.py ├── checks.py ├── context_processors.py ├── locking.py ├── manage_torrent.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── bibliotik_add_bulk.py │ │ ├── bibliotik_rebuild_fulltext.py │ │ ├── clear_what_login_cache.py │ │ ├── file_metadata_indexer.py │ │ ├── import_external_what_torrent.py │ │ ├── import_wm_media_folder.py │ │ ├── transmission_delete.py │ │ ├── transmission_files_sync.py │ │ ├── transmission_new.py │ │ ├── transmission_nice_reannounce.py │ │ ├── transmission_provision.py │ │ ├── transmission_start_all.py │ │ ├── transmission_stop_all.py │ │ ├── upload_manual_flac.py │ │ ├── what_data_updater.py │ │ └── what_meta_fixer.py ├── middleware.py ├── settings.example.py ├── templatetags │ ├── __init__.py │ └── custom_filters.py ├── tests.py ├── throttling.py ├── trans_sync.py ├── urls.py ├── utils.py ├── whatimg.py └── wsgi.py ├── bibliotik ├── .gitignore ├── __init__.py ├── admin.py ├── manage_bibliotik.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_bibliotiktorrentpagecache.py │ └── __init__.py ├── models.py ├── settings.example.py ├── trans_sync.py ├── urls.py ├── utils.py └── views.py ├── bibliotik_json ├── __init__.py ├── admin.py ├── maintenance_views.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── books ├── __init__.py ├── admin.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── settings.py ├── urls.py ├── utils.py ├── views.py └── what_upload.py ├── celery.sh ├── ci_setup.sh ├── deps ├── libevent-2.0.22-stable.tar.gz └── transmission-2.92.tar.xz ├── download ├── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── extra └── transcode-init.d │ ├── README.md │ └── wm2celery ├── home ├── __init__.py ├── admin.py ├── fixtures │ ├── replica_sets.json │ └── what_torrent_4795.json ├── info_holder.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_add_torrent_group_fk.py │ ├── 0003_file_metadata_cache_and_indexes.py │ ├── 0004_auto_20161206_1038.py │ ├── 0005_auto_20161206_1624.py │ └── __init__.py ├── models.py ├── parts.py ├── tests.py ├── urls.py └── views.py ├── integration_tests ├── test_basic_page_loads.py └── test_transmission_connectivity.py ├── login ├── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── manage.py ├── media ├── book_data │ └── .gitignore ├── qobuz_uploads │ └── .gitignore └── what_image_cache │ └── .gitignore ├── myanonamouse ├── .gitignore ├── __init__.py ├── manage_mam.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── settings.example.py ├── trans_sync.py ├── urls.py ├── utils.py └── views.py ├── player ├── __init__.py ├── models.py ├── player_utils.py ├── tests.py ├── urls.py └── views.py ├── qiller ├── .gitignore ├── __init__.py ├── download.py ├── make_torrent.py ├── metadata.py ├── prepare.py ├── qobuz_api.py ├── spectrals.py ├── tidal_api.py ├── upload.py ├── utils.py ├── what_api.py ├── what_upload.py └── whatimg.py ├── qobuz ├── .gitignore ├── __init__.py ├── admin.py ├── api.py ├── models.py ├── settings.example.py ├── tasks.py ├── tests.py ├── urls.py └── views.py ├── qobuz2 ├── .gitignore ├── __init__.py ├── admin.py ├── models.py ├── settings.example.py ├── tasks.py ├── templatetags │ ├── __init__.py │ └── qobuz2_filters.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── queue ├── __init__.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── parts.py ├── tests.py ├── urls.py └── views.py ├── requirements.txt ├── restart_wsgi.sh ├── setup.sh ├── setup_transmission-2.92.sh ├── static ├── UnicornAdmin.zip ├── css │ ├── bootstrap-glyphicons.css │ ├── bootstrap-slider.css │ ├── bootstrap-wysihtml5.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── font-awesome.css │ ├── main.css │ ├── player.css │ ├── unicorn-login.css │ └── unicorn.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── img │ ├── alpha.png │ ├── breadcrumb.png │ ├── empty-cover.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ ├── gritter-light.png │ ├── gritter-long.png │ ├── gritter.png │ ├── hue.png │ ├── ie-spacer.gif │ ├── larrow.png │ ├── line.png │ ├── logo-flac.png │ ├── logo-min.png │ ├── logo-mp3.png │ ├── logo.png │ ├── menu-active.png │ ├── pattern.png │ ├── rarrow.png │ ├── saturation.png │ ├── select2.png │ ├── select2x2.png │ ├── spinner.gif │ ├── sprite.png │ └── what_favicon.png ├── js │ ├── angular-route.min.js │ ├── angular-route.min.js.map │ ├── angular.min.js │ ├── angular.min.js.map │ ├── bootstrap-slider.min.js │ ├── bootstrap.js │ ├── excanvas.js │ ├── jquery-2.1.1.min.js │ ├── jquery-ui.min.js │ ├── jquery.colorhelpers.js │ ├── jquery.elevatezoom.js │ ├── jquery.flot.js │ ├── jquery.flot.labels.js │ ├── jquery.flot.pie.js │ ├── jquery.flot.resize.js │ ├── jquery.flot.time.js │ ├── jquery.multisortable.js │ ├── jquery.noty.packaged.min.js │ ├── jquery.peity.js │ ├── jquery.scrollintoview.js │ ├── jquery.typewatch.js │ ├── number-pb.js │ ├── spin.min.js │ ├── unicorn.dashboard.js │ ├── unicorn.js │ └── unicorn.login.js ├── player │ ├── aurora │ │ ├── aurora.js │ │ ├── aurora.js.map │ │ ├── flac.js │ │ ├── flac.js.map │ │ ├── mp3.js │ │ └── mp3.js.map │ ├── auroraplayer.js │ ├── buzz.js │ ├── dgplayer │ │ ├── README.md │ │ ├── player.css │ │ ├── player.html │ │ ├── player.js │ │ └── resources │ │ │ ├── bg.png │ │ │ ├── classlist.js │ │ │ ├── digital-7_mono.eot │ │ │ ├── digital-7_mono.ttf │ │ │ ├── digital-7_mono.woff │ │ │ ├── fallback_avatar.png │ │ │ ├── handle.png │ │ │ ├── nextbutton.png │ │ │ ├── nextbutton_active.png │ │ │ ├── pad.psd │ │ │ ├── pausebutton.png │ │ │ ├── pausebutton_active.png │ │ │ ├── playbutton.png │ │ │ ├── playbutton_active.png │ │ │ ├── prevbutton.png │ │ │ ├── prevbutton_active.png │ │ │ ├── ring.png │ │ │ ├── volume_high.png │ │ │ ├── volume_high.svg │ │ │ ├── volume_low.png │ │ │ └── volume_low.svg │ └── html5player.js └── whatify │ ├── app.js │ ├── home │ ├── albumInfo.html │ ├── albumPlaylist.html │ ├── artist.html │ ├── artistInfo.html │ ├── home.html │ ├── home.js │ ├── playlist.html │ └── torrentGroup.html │ ├── player │ ├── currentItemSidebar.html │ ├── player.html │ └── player.js │ ├── searchBar │ ├── searchBar.html │ └── searchBar.js │ ├── whatify.css │ └── whatify.less ├── templates ├── base.html ├── bibliotik │ ├── cache_worker.html │ ├── refresh_ui.html │ └── torrent_info_cell.html ├── books │ ├── edit_upload.html │ ├── new_upload.html │ └── uploads.html ├── dashboard_base.html ├── dashboard_search_redirect.html ├── download │ └── pls.txt ├── home │ ├── checks.html │ ├── dashboard.html │ ├── part │ │ ├── add_torrent.html │ │ ├── downloading_torrents.html │ │ ├── error_torrents.html │ │ ├── location_stats.html │ │ ├── recent_log.html │ │ ├── recently_downloaded.html │ │ ├── search_torrents.html │ │ ├── stats.html │ │ └── torrent_stats.html │ ├── part_ui │ │ ├── checks.html │ │ ├── downloading.html │ │ ├── error_torrents.html │ │ ├── recent_log.html │ │ ├── recently_downloaded.html │ │ ├── search_torrents.html │ │ ├── stats.html │ │ └── torrent_stats.html │ ├── stats.html │ ├── torrent_info_cell.html │ ├── torrents.html │ ├── userscripts.html │ └── view_log.html ├── js │ └── html5player.html ├── login │ └── login.html ├── player │ └── index.html ├── qobuz │ ├── edit_upload.html │ ├── new_upload.html │ └── uploads.html ├── qobuz2 │ ├── description_row.html │ ├── edit_upload.html │ ├── new_upload.html │ ├── pre_upload.html │ ├── spectrals_row.html │ ├── upload_whatcd.html │ └── uploads.html ├── queue │ ├── part │ │ ├── queue_pop.html │ │ └── queue_stats.html │ ├── part_ui │ │ ├── queue_pop.html │ │ └── queue_stats.html │ └── queue.html ├── userscript │ ├── BibliotikSessionStealer.crx │ ├── BibliotikSessionStealer.pem │ ├── BibliotikSessionStealer │ │ ├── background.js │ │ └── manifest.json │ ├── bibliotik.user.js │ ├── myanonamouse.user.js │ ├── overdrive.user.js │ └── what.cd.user.js ├── what_profile │ ├── part │ │ ├── profile_history.html │ │ ├── stats.html │ │ └── updown_graphs.html │ ├── part_ui │ │ └── profile_history.html │ └── profile.html ├── what_transcode │ ├── status.html │ └── status_table.html └── whatify │ └── index.html ├── test_integration.py ├── test_requirements.txt ├── tox.ini ├── userscript ├── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── wcd_pth_migration ├── __init__.py ├── admin.py ├── logfile.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── check_existing_spectrals.py │ │ ├── clear_pth_torrent_upload_status.py │ │ ├── export_torrent_data.py │ │ ├── init_torrent_group_mapping.py │ │ ├── migration_clear_whatmanager.py │ │ └── perform_pth_mass_upload.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_whattorrentmigrationstatus.py │ ├── 0003_auto_20161206_1055.py │ ├── 0004_whattorrentmigrationstatus_pth_torrent_id.py │ ├── 0005_auto_20161216_1701.py │ └── __init__.py ├── models.py ├── test.py ├── tests.py ├── torrentcheck.py ├── utils.py └── views.py ├── what_json ├── __init__.py ├── models.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── what_meta ├── __init__.py ├── admin.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_whatmetafulltext.py │ ├── 0003_add_artist_aliases.py │ ├── 0004_one_to_one_field.py │ └── __init__.py ├── models.py ├── urls.py └── views.py ├── what_profile ├── __init__.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_add_datetime_index.py │ └── __init__.py ├── models.py ├── parts.py ├── tests.py ├── urls.py └── views.py ├── what_transcode ├── __init__.py ├── flac_lame.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── tasks.py ├── tests.py ├── urls.py ├── utils.py └── views.py └── whatify ├── __init__.py ├── admin.py ├── filtering.py ├── migrations └── __init__.py ├── models.py ├── response_gen.py ├── tests.py ├── urls.py ├── utils.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | WhatManager2/settings.py 2 | 3 | .idea/workspace.xml 4 | .idea/tasks.xml 5 | .idea/dataSources.local.xml 6 | celeryev.pid 7 | 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | bin/ 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .cache 41 | nosetests.xml 42 | coverage.xml 43 | 44 | # Translations 45 | *.mo 46 | 47 | # Mr Developer 48 | .mr.developer.cfg 49 | .project 50 | .pydevproject 51 | 52 | # Rope 53 | .ropeproject 54 | 55 | # Django stuff: 56 | *.log 57 | *.pot 58 | 59 | # Sphinx documentation 60 | docs/_build/ 61 | 62 | # libevent and transmission build outputs 63 | libevent-*/* 64 | transmission-*/* 65 | 66 | # Vagrant 67 | .vagrant 68 | python2.7 69 | .venv/share 70 | pip-selfcheck.json 71 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "qiller"] 2 | path = qiller 3 | url = gogs@git.ub.vc:magicinc/qiller.git 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | WhatManager2 -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/dataSources.ids: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mysql 6 | com.mysql.jdbc.Driver 7 | jdbc:mysql://127.0.0.1/what_manager2 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/dictionaries/ivailo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bibliotik 5 | bitrate 6 | catno 7 | freeleech 8 | middleware 9 | rsync 10 | transcoding 11 | unescape 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/other.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/sqldialects.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/testrunner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | services: 5 | - "mysql" 6 | install: 7 | - "pip install -r test_requirements.txt" 8 | - "pip install -r requirements.txt" 9 | - "./setup_transmission-2.84.sh" 10 | - "./ci_setup.sh" 11 | - "mysql -e 'CREATE DATABASE `what_manager2` COLLATE utf8_unicode_ci;'" 12 | - "cp WhatManager2/settings.example.py WhatManager2/settings.py" 13 | - "cp bibliotik/settings.example.py bibliotik/settings.py" 14 | - "./manage.py migrate --noinput" 15 | - "sudo -- bash -c 'source /home/travis/virtualenv/python2.7/bin/activate && ./manage.py transmission_new --no-confirm what.cd'" 16 | - "sudo -- bash -c 'source /home/travis/virtualenv/python2.7/bin/activate && ./manage.py transmission_new --no-confirm bibliotik.me'" 17 | script: 18 | - "flake8 ." 19 | - "./manage.py test" 20 | - "./test_integration.py" 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "${workspaceFolder}/.venv/bin/python" 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ivailo Karamanolev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | bencode = ">=1.0" 8 | django-celery = ">=3.0.23" 9 | mutagen = ">=1.22" 10 | pyquery = ">=1.2.4" 11 | pytz = ">=2013.7" 12 | transmissionrpc = ">=0.10" 13 | requests = ">=2.0.0" 14 | kombu = "<4.0.0,>=3.0.15" 15 | billiard = ">=3.3.0.17" 16 | django-bootstrap-form = ">=3.1" 17 | sqlparse = ">=0.1.11" 18 | ujson = ">=1.33" 19 | celery = "<4.0" 20 | "html2bbcode" = "==2.3.2" 21 | imgurpython = "==1.1.7" 22 | numpy = "==1.11.2" 23 | Django = "<1.9,>=1.8.17" 24 | MySQL-python = ">=1.2.4" 25 | Pillow = "==3.4.2" 26 | 27 | [dev-packages] 28 | pylint = "*" 29 | rope = "*" 30 | yapf = "*" 31 | 32 | [requires] 33 | python_version = "2.7" 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WhatManager2 2 | ============ 3 | 4 | Torrent management system based on Django and Transmission 5 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | config.vm.box = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-i386-vagrant-disk1.box" 9 | 10 | config.vm.network "forwarded_port", guest: 80, host: 8080 11 | config.vm.network "private_network", ip: "192.168.33.10" 12 | 13 | # config.vm.synced_folder "../data", "/vagrant_data" 14 | end 15 | 16 | # What Manager 2 setup 17 | 18 | $script = < 40 | 65 | 66 | -------------------------------------------------------------------------------- /static/player/dgplayer/resources/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/bg.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/classlist.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal classList shim for IE 9 3 | * By Devon Govett 4 | * MIT LICENSE 5 | */ 6 | 7 | 8 | if (!("classList" in document.documentElement) && Object.defineProperty && typeof HTMLElement !== 'undefined') { 9 | Object.defineProperty(HTMLElement.prototype, 'classList', { 10 | get: function() { 11 | var self = this; 12 | function update(fn) { 13 | return function(value) { 14 | var classes = self.className.split(/\s+/), 15 | index = classes.indexOf(value); 16 | 17 | fn(classes, index, value); 18 | self.className = classes.join(" "); 19 | } 20 | } 21 | 22 | var ret = { 23 | add: update(function(classes, index, value) { 24 | ~index || classes.push(value); 25 | }), 26 | 27 | remove: update(function(classes, index) { 28 | ~index && classes.splice(index, 1); 29 | }), 30 | 31 | toggle: update(function(classes, index, value) { 32 | ~index ? classes.splice(index, 1) : classes.push(value); 33 | }), 34 | 35 | contains: function(value) { 36 | return !!~self.className.split(/\s+/).indexOf(value); 37 | }, 38 | 39 | item: function(i) { 40 | return self.className.split(/\s+/)[i] || null; 41 | } 42 | }; 43 | 44 | Object.defineProperty(ret, 'length', { 45 | get: function() { 46 | return self.className.split(/\s+/).length; 47 | } 48 | }); 49 | 50 | return ret; 51 | } 52 | }); 53 | } -------------------------------------------------------------------------------- /static/player/dgplayer/resources/digital-7_mono.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/digital-7_mono.eot -------------------------------------------------------------------------------- /static/player/dgplayer/resources/digital-7_mono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/digital-7_mono.ttf -------------------------------------------------------------------------------- /static/player/dgplayer/resources/digital-7_mono.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/digital-7_mono.woff -------------------------------------------------------------------------------- /static/player/dgplayer/resources/fallback_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/fallback_avatar.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/handle.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/nextbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/nextbutton.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/nextbutton_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/nextbutton_active.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/pad.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/pad.psd -------------------------------------------------------------------------------- /static/player/dgplayer/resources/pausebutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/pausebutton.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/pausebutton_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/pausebutton_active.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/playbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/playbutton.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/playbutton_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/playbutton_active.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/prevbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/prevbutton.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/prevbutton_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/prevbutton_active.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/ring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/ring.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/volume_high.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/volume_high.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/volume_high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /static/player/dgplayer/resources/volume_low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/static/player/dgplayer/resources/volume_low.png -------------------------------------------------------------------------------- /static/player/dgplayer/resources/volume_low.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /static/whatify/home/albumPlaylist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 20 | 23 | 28 | 31 | 32 | 33 |
Track
14 | 15 | 16 | 17 | 18 | {{ $index + 1 }} 19 | 21 | {{ item.metadata.title }} 22 | 24 | 27 | 29 | {{ item.metadata.duration | asTime }} 30 |
34 | -------------------------------------------------------------------------------- /static/whatify/home/artist.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

{{ category.name }}

6 | 7 |
8 |
9 |
11 |
12 |
13 | 14 |

15 | 17 | View on What.CD 18 | 19 | 20 | Refresh Data 21 | 22 |

23 |
24 | -------------------------------------------------------------------------------- /static/whatify/home/artistInfo.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
{{ artist.name }}
6 |
7 | 8 | {{ tag.name }}{{ $last ? '' : ', ' }} 9 | 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /static/whatify/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |

Top 10

3 | 4 |
5 |
6 |
7 | 8 |
9 | 10 |

Random Albums

11 | 12 |
13 |
14 |
15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /static/whatify/home/playlist.html: -------------------------------------------------------------------------------- 1 |
2 |

Playlist

3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 25 | 28 | 31 | 36 | 39 | 40 | 41 |
TrackArtistAlbum
19 | 20 | 21 | 22 | 23 | {{ $index + 1 }} 24 | 26 | {{ item.metadata.title }} 27 | 29 | 30 | 32 | 33 | {{ item.torrentGroup.name }} 34 | 35 | 37 | {{ item.metadata.duration | asTime }} 38 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /static/whatify/home/torrentGroup.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
6 | 7 |

8 | 9 |

10 | 12 | View on What.CD 13 | 14 | 15 | Refresh Data 16 | 17 |

18 |
19 | -------------------------------------------------------------------------------- /static/whatify/player/currentItemSidebar.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |

{{ item.metadata.title }}

7 | 8 |

9 | 10 | {{ item.torrentGroup.name }} 11 | 12 |

13 | 14 |

15 | 16 |

17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /static/whatify/player/player.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | {{ player.currentTime | asTime }} 26 | 27 | 29 | 30 | 31 | {{ player.duration | asTime }} 32 | 33 | 34 | -------------------------------------------------------------------------------- /static/whatify/searchBar/searchBar.html: -------------------------------------------------------------------------------- 1 | 3 |
4 | 32 |
33 | -------------------------------------------------------------------------------- /static/whatify/searchBar/searchBar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular. 4 | module('whatify.searchBar', ['whatify']). 5 | controller('SearchController', function($scope, whatMeta) { 6 | $scope.resultsVisible = false; 7 | $scope.searchQuery = ''; 8 | $scope.search = function() { 9 | if ($scope.searchQuery.length > 2) { 10 | whatMeta.search($scope.searchQuery).success(function(response) { 11 | $scope.searchResults = response; 12 | }); 13 | } 14 | }; 15 | $scope.hideResults = function() { 16 | $scope.resultsVisible = false; 17 | }; 18 | $scope.showResults = function() { 19 | $scope.resultsVisible = true; 20 | } 21 | }). 22 | directive('ngWmSearchBar', function($document) { 23 | return { 24 | templateUrl: templateRoot + '/searchBar/searchBar.html', 25 | controller: 'SearchController', 26 | link: function(scope, element, attrs) { 27 | $document.on('click', function(e) { 28 | scope.$apply(function() { 29 | scope.hideResults(); 30 | }) 31 | }); 32 | 33 | element.find('.search-results, input').on('click', function(e) { 34 | e.stopPropagation(); 35 | }); 36 | 37 | scope.$watch('resultsVisible', function(newValue, oldValue) { 38 | if (newValue) { 39 | element.find('.search-results').fadeIn('fast'); 40 | } else { 41 | element.find('.search-results').fadeOut('fast'); 42 | } 43 | }); 44 | } 45 | } 46 | }) 47 | ; 48 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | 6 | What Manager 2 7 | 8 | 9 | 10 | {% block head_styles %} 11 | 12 | 13 | 14 | 15 | 16 | {% endblock %} 17 | 18 | 19 | 20 | {% block body %}{% endblock %} 21 | 22 | {% block body_scripts %} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {% endblock %} 33 | 34 | 35 | -------------------------------------------------------------------------------- /templates/bibliotik/torrent_info_cell.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 |
4 | 5 | {% if bibliotik_torrent.category == "Ebooks" %} 6 | {{ bibliotik_torrent.author }} - {{ bibliotik_torrent.title }} 7 | [{{ bibliotik_torrent.year }}] 8 | [{{ bibliotik_torrent.format }}] 9 | {% else %} 10 | [Unknown Type] {{ bibliotik_torrent.title }} 11 | {% endif %} 12 | 13 |
14 | 15 |
16 | {% if bibliotik_torrent.category == "Ebooks" %} 17 | {% if bibliotik_torrent.retail %} 18 | 19 | RETAIL 20 |   21 | {% endif %} 22 | {% if bibliotik_torrent.pages %} 23 | {{ bibliotik_torrent.pages }} pages / 24 | {% endif %} 25 | {{ bibliotik_torrent.language }} 26 | {% else %} 27 | {{ bibliotik_torrent.language }} 28 | {% endif %} 29 |
30 | 31 |
32 | {% if bibliotik_torrent.category == "Ebooks" %} 33 | {{ bibliotik_torrent.publisher_list|join:", " }} / 34 | {{ bibliotik_torrent.isbn }} / 35 | {{ bibliotik_torrent.tags }} 36 | {% endif %} 37 |
38 | -------------------------------------------------------------------------------- /templates/dashboard_search_redirect.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/download/pls.txt: -------------------------------------------------------------------------------- 1 | {% autoescape off %} 2 | [playlist] 3 | Version=2 4 | NumberOfEntries={{ files|length }} 5 | {% for f in files %} 6 | File{{ forloop.counter }}={{ f.path }} 7 | Title{{ forloop.counter }}={{ f.artist }} - {{ f.title }} 8 | Length{{ forloop.counter }}={{ f.duration|floatformat:"0" }} 9 | {% endfor %} 10 | {% endautoescape %} -------------------------------------------------------------------------------- /templates/home/checks.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Checks

8 |
9 | 13 |
14 |
15 |
16 |
17 | {% if perms.home.run_checks %} 18 |
19 |
Checks
20 |
21 |
22 |

Loading...

23 |
24 | {% else %} 25 |
26 |
No permission
27 |
28 |
29 | You don't have permission to run the checks. 30 |
31 | {% endif %} 32 |
33 |
34 |
35 |
36 | {% endblock %} 37 | 38 | {% block body_scripts %} 39 | {{ block.super }} 40 | 41 | {% include 'dashboard_search_redirect.html' %} 42 | {% if perms.home.run_checks %} 43 | 50 | {% endif %} 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /templates/home/dashboard.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load staticfiles custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Dashboard

8 |
9 | 13 |
14 |
15 | {% include 'home/part/torrent_stats.html' %} 16 | {% include 'home/part/add_torrent.html' %} 17 |
18 | 19 |
20 | {% include 'home/part/location_stats.html' %} 21 |
22 | 23 | {% include 'home/part/recent_log.html' %} 24 |
25 | {% endblock %} 26 | 27 | {% block body_scripts %} 28 | {{ block.super }} 29 | 30 | {% include 'dashboard_search_redirect.html' %} 31 | {% endblock %} -------------------------------------------------------------------------------- /templates/home/part/downloading_torrents.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {% if perms.home.view_whattorrent %} 5 |
6 | 7 |
Downloading Torrents
8 |
9 |
10 |
11 | {% else %} 12 |
13 |
No permission
14 |
15 |
16 | You don't have permission to view torrents. 17 |
18 | {% endif %} 19 |
20 |
21 |
22 | 23 | {% if perms.home.view_whattorrent %} 24 | 39 | {% endif %} 40 | -------------------------------------------------------------------------------- /templates/home/part/error_torrents.html: -------------------------------------------------------------------------------- 1 | {% if perms.home.view_whattorrent %} 2 | 19 | 20 | 35 | {% endif %} 36 | -------------------------------------------------------------------------------- /templates/home/part/location_stats.html: -------------------------------------------------------------------------------- 1 | {% for location in locations %} 2 |
3 |
4 |
5 |
{{ location.path|cut:"/mnt/"|cut:"Torrent/" }} 6 | ({{ location.torrent_count }})
7 |
8 |
9 |
10 |
11 |
12 |
13 | 14 | 39 | {% endfor %} 40 | 41 | -------------------------------------------------------------------------------- /templates/home/part/recent_log.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
Recent log entries
6 | 7 |
8 | 11 | 14 | 17 |
18 |
19 |
20 |
21 |
22 |
23 | 46 |
47 | 48 | -------------------------------------------------------------------------------- /templates/home/part/recently_downloaded.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {% if perms.home.view_whattorrent %} 5 |
6 | 7 |
Recently Downloaded
8 |
9 |
10 |
11 | {% else %} 12 |
13 |
No permission
14 |
15 |
16 | You don't have permission to view torrents. 17 |
18 | {% endif %} 19 |
20 |
21 |
22 | 23 | {% if perms.home.view_whattorrent %} 24 | 30 | {% endif %} 31 | -------------------------------------------------------------------------------- /templates/home/part/search_torrents.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /templates/home/part/stats.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 8 | -------------------------------------------------------------------------------- /templates/home/part/torrent_stats.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Site Statistics
5 |
6 |
7 |
8 |
9 |
10 | 11 | -------------------------------------------------------------------------------- /templates/home/part_ui/checks.html: -------------------------------------------------------------------------------- 1 | 2 | {% if traceback %} 3 | 4 | 7 | 8 | {% elif errors or warnings %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% for error in errors %} 16 | 17 | 18 | 19 | {% endfor %} 20 | {% for warning in warnings %} 21 | 22 | 23 | 24 | {% endfor %} 25 | 26 | {% else %} 27 | 28 | 31 | 32 | {% endif %} 33 |
5 | {{ traceback }} 6 |
Message
{{ error }}
{{ warning }}
29 |

Pass

30 |
-------------------------------------------------------------------------------- /templates/home/part_ui/recent_log.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% for e in log_entries %} 13 | 19 | 20 | 21 | 22 | 23 | 24 | {% endfor %} 25 | 26 |
TypeUserDateMessage
{{ e.type }}{{ e.user.username }}{{ e.datetime }}{{ e.message }}
-------------------------------------------------------------------------------- /templates/home/part_ui/torrent_stats.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 | 14 | -------------------------------------------------------------------------------- /templates/home/stats.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load staticfiles custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Stats

8 |
9 | 13 |
14 | {% include 'home/part/stats.html' %} 15 | 16 |
17 | {% endblock %} 18 | 19 | {% block body_scripts %} 20 | {{ block.super }} 21 | 22 | {% include 'dashboard_search_redirect.html' %} 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/home/torrent_info_cell.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 |
4 | 5 | {% if what_torrent.info_loads.group.categoryName == "Music" %} 6 | {{ what_torrent.info_artist }} - {{ what_torrent.info_title }} 7 | [{{ what_torrent.info_year }}] 8 | [{{ what_torrent.info_release_type_name }}] 9 | {% elif what_torrent.info_loads.group.categoryName == "E-Books" %} 10 | [E-Books] 11 | {{ what_torrent.info_title }} 12 | {% else %} 13 | [Unknown Type] {{ what_torrent.info_title }} 14 | {% endif %} 15 | 16 |
17 | 18 |
19 | {% if what_torrent.info_loads.group.categoryName == "Music" %} 20 | {% if what_torrent.info_has_artwork %} 21 | 23 | +ARTWORK 24 |   25 | {% endif %} 26 | {{ what_torrent.info_media }} / 27 | {{ what_torrent.info_format }} / 28 | {{ what_torrent.info_encoding }} / 29 | {{ what_torrent.info_size|filesizeformat }} 30 | {% elif what_torrent.info_loads.group.categoryName == "E-Books" %} 31 | {{ what_torrent.info_size|filesizeformat }} 32 | {% else %} 33 | {{ what_torrent.info_size|filesizeformat }} 34 | {% endif %} 35 |
36 | 37 |
38 | {% if what_torrent.info_loads.group.categoryName == "Music" %} 39 | {% if what_torrent.info_label %} 40 | | 41 | {{ what_torrent.info_label }} 42 | {% endif %} 43 | 44 | {% if what_torrent.info_catno %} 45 | | 46 | {{ what_torrent.info_catno }} 47 | {% endif %} 48 | 49 | {% if what_torrent.info_remaster_title %} 50 | | 51 | {{ what_torrent.info_remaster_title }} 52 | {% endif %} 53 | {% elif what_torrent.info_loads.group.categoryName == "E-Books" %} 54 | {{ what_torrent|torrent_files }} 55 | {% endif %} 56 |
57 | -------------------------------------------------------------------------------- /templates/home/torrents.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Torrents

8 |
9 | 13 |
14 | {% include 'home/part/search_torrents.html' %} 15 | 16 | {% include 'home/part/error_torrents.html' %} 17 | 18 | {% include 'home/part/downloading_torrents.html' %} 19 | 20 | {% include 'home/part/recently_downloaded.html' %} 21 |
22 | {% endblock %} 23 | 24 | {% block body_scripts %} 25 | {{ block.super }} 26 | 27 | 30 | {% endblock %} -------------------------------------------------------------------------------- /templates/home/userscripts.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load staticfiles custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Userscripts

8 |
9 | 13 |
14 |
15 |
16 |
17 |
18 | 19 |
Userscripts
20 |
21 |
22 |

23 | What.CD Integration 24 |

25 | 26 |

27 | OverDrive Searching 28 |

29 | 30 |

31 | Bibliotik Integration 32 |

33 | 34 |

35 | MyAnonaMouse Integration 36 |

37 |
38 |
39 |
40 |
41 |
42 | {% endblock %} 43 | 44 | {% block body_scripts %} 45 | {{ block.super }} 46 | 47 | {% include 'dashboard_search_redirect.html' %} 48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /templates/home/view_log.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Log

8 |
9 | 13 |
14 | {% include 'home/part/recent_log.html' with recent_log_count=100 %} 15 |
16 | {% endblock %} 17 | 18 | {% block body_scripts %} 19 | {{ block.super }} 20 | 21 | {% include 'dashboard_search_redirect.html' %} 22 | 25 | {% endblock %} -------------------------------------------------------------------------------- /templates/js/html5player.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 6 | -------------------------------------------------------------------------------- /templates/login/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% load staticfiles %} 4 | 5 | {% block head_styles %} 6 | {{ block.super }} 7 | 8 | {% endblock %} 9 | 10 | {% block body %} 11 |
12 | 15 |
16 |
17 |

Hello,

18 |
19 |
20 | 21 |
22 |
23 | {% csrf_token %} 24 | 25 |

Enter username and password to continue.

26 | 27 |
28 | 29 | 31 |
32 | 33 |
34 | 35 | 37 |
38 | 39 |
40 | 41 |
42 |
43 |
44 |
45 | {% endblock %} 46 | 47 | {% block body_scripts %} 48 | {{ block.super }} 49 | 50 | {% endblock %} -------------------------------------------------------------------------------- /templates/qobuz/new_upload.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load staticfiles custom_filters bootstrap %} 4 | 5 | {% block content %} 6 |
7 |

New Upload

8 |
9 | 14 | 15 |
16 |
17 |
18 |
19 |
20 | 21 |
Qobuz Album
22 |
23 |
24 |
26 | {% csrf_token %} 27 | {{ form|bootstrap }} 28 |
29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {% endblock %} 38 | 39 | {% block body_scripts %} 40 | {{ block.super }} 41 | 42 | {% include 'dashboard_search_redirect.html' %} 43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /templates/qobuz2/description_row.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |
Generated Description
7 |
8 |
9 | 11 |
12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /templates/qobuz2/new_upload.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load staticfiles custom_filters bootstrap %} 4 | 5 | {% block content %} 6 |
7 |

New Upload

8 |
9 | 14 | 15 |
16 |
17 |
18 |
19 |
20 | 21 |
Qobuz Album
22 |
23 |
24 |
26 | {% csrf_token %} 27 | {{ form|bootstrap }} 28 |
29 | 33 | 37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {% endblock %} 45 | 46 | {% block body_scripts %} 47 | {{ block.super }} 48 | 49 | {% include 'dashboard_search_redirect.html' %} 50 | {% endblock %} 51 | -------------------------------------------------------------------------------- /templates/qobuz2/pre_upload.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load staticfiles custom_filters bootstrap %} 4 | 5 | {% block content %} 6 | 11 |
12 |

Prepare Qobuz Upload

13 |
14 | 21 | 22 |
23 |
24 |
25 |
26 |
27 | 28 |
Upload Details
29 |
30 | 36 |
37 |
38 |
39 | 40 | {% include 'qobuz2/spectrals_row.html' %} 41 | 42 | {% include 'qobuz2/description_row.html' %} 43 |
44 | {% endblock %} 45 | 46 | {% block body_scripts %} 47 | {{ block.super }} 48 | 49 | {% include 'dashboard_search_redirect.html' %} 50 | {% endblock %} 51 | -------------------------------------------------------------------------------- /templates/qobuz2/spectrals_row.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | {% if spectrals %} 3 | 4 |
5 |
6 |
7 |
8 | 9 |
Spectrals
10 |
11 |
12 | {% for file in spectrals %} 13 |

14 | 16 |

17 | {% endfor %} 18 |
19 |
20 |
21 |
22 | 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /templates/queue/part/queue_pop.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
Queue Pop
6 | 7 |
8 | 12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /templates/queue/part/queue_stats.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
Queue Statistics
6 |
7 |
8 |
9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /templates/queue/part_ui/queue_pop.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 | {% if front %} 4 |

5 | {{ front.artist }}
6 | {{ front.title }} 7 |

8 | 9 |

10 | ID: {{ front.what_id }}
11 | {{ front.release_type|release_type_name }}
12 | {{ front.format }}
13 | {{ front.encoding }}
14 | {{ front.torrent_size|filesizeformat }} 15 |

16 | {% else %} 17 | Queue is empty. 18 | {% endif %} -------------------------------------------------------------------------------- /templates/queue/part_ui/queue_stats.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 | 14 | -------------------------------------------------------------------------------- /templates/queue/queue.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load staticfiles custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Queue

8 |
9 | 13 |
14 | {% if perms.queue.view_queueitem %} 15 |
16 | {% include 'queue/part/queue_stats.html' %} 17 | {% include 'home/part/location_stats.html' %} 18 | {% include 'queue/part/queue_pop.html' %} 19 |
20 | 21 | {% include 'home/part/downloading_torrents.html' %} 22 | 23 | {% include 'home/part/recently_downloaded.html' %} 24 | {% else %} 25 |
26 |
27 |
28 |
29 | 30 |
No permission
31 |
32 |
33 | You don't have permission to view the queue. 34 |
35 |
36 |
37 |
38 | {% endif %} 39 |
40 | {% endblock %} 41 | 42 | {% block body_scripts %} 43 | {{ block.super }} 44 | 45 | {% include 'dashboard_search_redirect.html' %} 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /templates/userscript/BibliotikSessionStealer.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/templates/userscript/BibliotikSessionStealer.crx -------------------------------------------------------------------------------- /templates/userscript/BibliotikSessionStealer.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCgWzDbW/IVnLl1 3 | DEJ7UcRi6Z+gXIKAfrzjOYi9qA1AdMK3y8M9achWpuULEP6sXyQMW22hsiaNrxq+ 4 | ocxSF/04lAbM9rX64yAtEi08Rt3pSsCrBK0yphyiCe5bPPchxU729vFbuVuMTGc7 5 | VhsEd/6GCgWS9CXq1jEFo3VA5q/7k4XhiQLaXC/tevNERLVhKV8Wya2FBtevNVM3 6 | gvYTqAK8foUTLsX57lTi1O3mflPKr0Z7IV4QxiAwfPD1ahsJOW0BQ/8JpyERTHIV 7 | gKrh6UXTb79QhYGT7IQgvyHMHRVResqEzgGmPjIIro50JPywp/0Q9jsqGC92/Qon 8 | Q37Our1ZAgMBAAECggEBAJzGy0uL53Et2zwOkmkTFlhGZSRZjdWnE3FsbL5QMTSV 9 | c1JSyz2d0Jq3bTOWmAdatVnHgbYXAQ6A5jr+4U5Z2d6ZCtUVV/JEYM7v+H34IznV 10 | Ne1+Ev9z3IaoAvv87Pl9eKiH4o10+YZ/pGYk1yFVV+kgnzZ/sRqZ8y+6Egc0A1aH 11 | 5qUI6kgERMw7AKR3hmTVWj5Kbp+DSBZO0GIVLirZUYMmcv49mmCnEd4vRuQR2rTT 12 | VUAvnWpatY/hOOVOOh6QLp6qbv2B+SwuPme8WHikf3lVqxVL8o3zozubcbn/oGw3 13 | gcwh3lBDOOhR6js7iVxLf83pxtgp4ecLnbQxQPaRAekCgYEAzRNCONO3DZLAqYZV 14 | BmIwy+HNMEQhRcxg0p/TJiP5j5BcJaSRON2rugwTGKhjzsItbXGvcPUlC1lOXytY 15 | wZKqJLgZDzBOFyMzcpiB3qNRmKvpHtmvm7KBa60TuM/nzlrmMxy17y7tgWaVAL7A 16 | eExt+WtpvzCZICAbK4vhVAizxTMCgYEAyC0e4Meo4OyPU4RKTai6THlkejWQ9RGr 17 | F2gUXfKsCxEgKrC4ekRkWQWgWvqDx2LDQHqtuwvCUsQ/qJvcV095xkXSyMIpqrYo 18 | Gy8vrznhzmN98no+VG7gxU2RIZLwvys5Tg+U6wHWPizlYz23SHgvIWQAki5gU8K0 19 | HIk/pHMXW0MCgYAq5PhZAo+rWyLJUru+FhO20YglrFp5SwKFi8CSnbu5d6yOgxmN 20 | F3Cv/Y6Q/cfCm4L8/WaA7gXNleR6q4ppEtBb1dYIA0aSOF6ufY/MjcSndm3Qch/1 21 | JSVE7HLPmU0XMJQt5Ld5TAuJoXdRLJT9lJ78KKd5wiSFP7HvLdb3yMoxWQKBgQDD 22 | DQxAdOx+tqL1+zq3r0hQyhx45Icp9Rxkg1sNLDAt3HMehfJL2SZ6g/v7io0rlHWy 23 | 95cfYlbodSeecLjkLqH27AR28JLPMA2mZ3UsZdc8Vz/bLPhdJ2lHd8yKDXcen8yH 24 | 3rD5yWFsVizyZamZKNqry5iu2BeuplMAV1A3OpowpwKBgQC09oi7JrBlE5EVbB0G 25 | kZUBhBrrZUBpjLR9I8L0M004dfypLkoRWOvn0jO3nlamRby9tlixhfOJCxXZ7SiL 26 | vZV3jayUZx3lDDy/lct4BRIegts/1gXXccJJO/FE0XhRSKJUg1BRxdO9E4m3ZtWK 27 | bO+hnFDCUuNrivHXOUmZlQ6hyA== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /templates/userscript/BibliotikSessionStealer/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) { 2 | if (request != "stealBibliotikId") { 3 | return; 4 | } 5 | chrome.cookies.getAll({}, function(cookies) { 6 | for (var i in cookies) { 7 | var cookie = cookies[i]; 8 | if (cookie.domain == ".bibliotik.me" && cookie.name == "id") { 9 | sendResponse(cookie.value); 10 | return; 11 | } 12 | } 13 | sendResponse(null); 14 | }); 15 | return true; 16 | }); 17 | -------------------------------------------------------------------------------- /templates/userscript/BibliotikSessionStealer/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Bibliotik Session ID Stealer", 4 | "description": "This extension provides Bibliotik integration.", 5 | "version": "0.0.2", 6 | "permissions": [ 7 | "cookies", 8 | "*://*.bibliotik.me/" 9 | ], 10 | "externally_connectable": { 11 | "matches": ["*://*.lib.overdrive.com/*", "https://*.karamanolev.com/*", "*://*.bibliotik.me/*", "*://localhost:*/*] 12 | }, 13 | "background": { 14 | "persistent": false, 15 | "scripts": ["background.js"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /templates/what_profile/part/profile_history.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |
Log Entries
7 |
8 |
9 |
10 |
11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /templates/what_profile/part/stats.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 |
4 |
5 | 37 |
38 |
-------------------------------------------------------------------------------- /templates/what_profile/part_ui/profile_history.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% for s in snapshots %} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% endfor %} 24 | 25 |
TimeUploadDownloadBufferRatio
{{ s.datetime|date:'Y-m-d H:i' }}{{ s.uploaded|filesizeformat }} ({{ s.total_uploaded|filesizeformat }}){{ s.downloaded|filesizeformat }} ({{ s.total_downloaded|filesizeformat }}){{ s.buffer|filesizeformat }} ({{ s.total_buffer|filesizeformat }}){{ s.ratio|floatformat:4 }} ({{ s.total_ratio|floatformat:4 }})
26 | -------------------------------------------------------------------------------- /templates/what_profile/profile.html: -------------------------------------------------------------------------------- 1 | {% extends 'dashboard_base.html' %} 2 | 3 | {% load custom_filters %} 4 | 5 | {% block content %} 6 |
7 |

Profile

8 |
9 | 13 |
14 | {% if perms.what_profile.view_whatusersnapshot %} 15 | {% include 'what_profile/part/stats.html' %} 16 | 17 | {% include 'what_profile/part/updown_graphs.html' %} 18 | 19 | {% include 'what_profile/part/profile_history.html' %} 20 | {% else %} 21 |
22 |
23 |
24 |
25 |
No permission
26 |
27 |
28 | You don't have permission to view the profile. 29 |
30 |
31 |
32 |
33 | {% endif %} 34 |
35 | {% endblock %} 36 | 37 | {% block body_scripts %} 38 | {{ block.super }} 39 | 40 | {% include 'dashboard_search_redirect.html' %} 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /templates/what_transcode/status_table.html: -------------------------------------------------------------------------------- 1 | {% load custom_filters %} 2 | 3 | {% for t_r in requests %} 4 | 5 | 6 | 7 | [what.cd: {{ t_r.what_torrent_id }}] 8 | 9 | 10 | 11 | {{ t_r.requested_by_what_user }} 12 | 13 | 14 | {{ t_r.what_torrent.joined_artists }} 15 | - 16 | {{ t_r.what_torrent.info_loads.group.name|safe }} 17 | 18 | {% if t_r.what_torrent.info_loads.torrent.remastered and t_r.what_torrent.info_loads.torrent.remasterYear != t_r.what_torrent.info_loads.group.year %} 19 | - {{ t_r.what_torrent.info_loads.torrent.remasterYear }} 20 | ({{ t_r.what_torrent.info_loads.group.year }}) 21 | {% else %} 22 | - {{ t_r.what_torrent.info_loads.group.year }} 23 | {% endif %} 24 | 25 | 26 | {{ t_r.what_torrent.info_loads.torrent.size|filesizeformat }} 27 | 28 | 29 | {% if t_r.show_retry_button %} 30 | 31 | {% endif %} 32 | {{ t_r.status|linebreaksbr }} 33 | 34 | 35 | {% endfor %} 36 | -------------------------------------------------------------------------------- /test_integration.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | import django 6 | from django.test.utils import setup_test_environment 7 | from django.utils import unittest 8 | 9 | 10 | os.environ['DJANGO_SETTINGS_MODULE'] = 'WhatManager2.settings' 11 | 12 | if __name__ == '__main__': 13 | setup_test_environment() 14 | django.setup() 15 | 16 | suite = unittest.TestLoader().discover('integration_tests') 17 | result = unittest.TextTestRunner(verbosity=2).run(suite) 18 | sys.exit(not result.wasSuccessful()) 19 | -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | flake8>=2.2.3 2 | mock>=1.0.1 3 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length=100 3 | exclude=libevent-2.0.21-stable/*,transmission-2.84/* 4 | -------------------------------------------------------------------------------- /userscript/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/userscript/__init__.py -------------------------------------------------------------------------------- /userscript/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/userscript/models.py -------------------------------------------------------------------------------- /userscript/tests.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/userscript/tests.py -------------------------------------------------------------------------------- /userscript/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns( 4 | '', 5 | url(r'^bibliotik.user.js$', 'userscript.views.bibliotik'), 6 | url(r'^what.cd.user.js$', 'userscript.views.whatcd'), 7 | url(r'^overdrive.user.js$', 'userscript.views.overdrive'), 8 | url(r'^myanonamouse.user.js$', 'userscript.views.myanonamouse'), 9 | ) 10 | -------------------------------------------------------------------------------- /userscript/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | from WhatManager2.settings import USERSCRIPT_WM_ROOT 4 | 5 | 6 | def bibliotik(request): 7 | data = { 8 | 'root': USERSCRIPT_WM_ROOT 9 | } 10 | return render(request, 'userscript/bibliotik.user.js', data, content_type='text/javascript') 11 | 12 | 13 | def whatcd(request): 14 | data = { 15 | 'root': USERSCRIPT_WM_ROOT 16 | } 17 | return render(request, 'userscript/what.cd.user.js', data, content_type='text/javascript') 18 | 19 | 20 | def overdrive(request): 21 | data = { 22 | 'root': USERSCRIPT_WM_ROOT 23 | } 24 | return render(request, 'userscript/overdrive.user.js', data, content_type='text/javascript') 25 | 26 | 27 | def myanonamouse(request): 28 | data = { 29 | 'root': USERSCRIPT_WM_ROOT 30 | } 31 | return render(request, 'userscript/myanonamouse.user.js', data, content_type='text/javascript') 32 | 33 | -------------------------------------------------------------------------------- /wcd_pth_migration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/wcd_pth_migration/__init__.py -------------------------------------------------------------------------------- /wcd_pth_migration/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /wcd_pth_migration/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/wcd_pth_migration/management/__init__.py -------------------------------------------------------------------------------- /wcd_pth_migration/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/wcd_pth_migration/management/commands/__init__.py -------------------------------------------------------------------------------- /wcd_pth_migration/management/commands/check_existing_spectrals.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | from django.core.management.base import BaseCommand 5 | 6 | from home.models import ReplicaSet, DownloadLocation 7 | from wcd_pth_migration.utils import generate_spectrals_for_dir 8 | 9 | 10 | class Command(BaseCommand): 11 | help = 'Clears the database of what torrents, trans torrents, also removes all torrents from transmission' 12 | 13 | def handle(self, *args, **options): 14 | checked = [] 15 | try: 16 | with open('checked.json', 'r') as f: 17 | checked = json.loads(f.read()) 18 | except IOError: 19 | pass 20 | for dl in DownloadLocation.objects.filter(zone=ReplicaSet.ZONE_WHAT): 21 | for torrent in os.listdir(dl.path): 22 | if torrent in checked: 23 | print 'Already checked', torrent, 'skipping...' 24 | continue 25 | torrent_path = os.path.join(dl.path, torrent) 26 | print 'Generating spectrals for', torrent_path 27 | if not generate_spectrals_for_dir(torrent_path): 28 | print 'There are no FLACs, moving on...' 29 | else: 30 | raw_input('Please check the spectrals...') 31 | checked.append(torrent) 32 | with open('checked.json', 'w') as f: 33 | f.write(json.dumps(checked)) 34 | -------------------------------------------------------------------------------- /wcd_pth_migration/management/commands/clear_pth_torrent_upload_status.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from wcd_pth_migration.models import WhatTorrentMigrationStatus 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'Saves the current complete uploads as WhatTorrentGroupMatching objects' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument('what_torrent_id') 11 | 12 | def handle(self, *args, **options): 13 | torrent_id = options['what_torrent_id'] 14 | WhatTorrentMigrationStatus.objects.get(what_torrent_id=torrent_id).delete() 15 | -------------------------------------------------------------------------------- /wcd_pth_migration/management/commands/export_torrent_data.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.core.management.base import BaseCommand 4 | from django.forms.models import model_to_dict 5 | 6 | from home.models import ReplicaSet 7 | 8 | 9 | def date_handler(obj): 10 | if hasattr(obj, 'isoformat'): 11 | return obj.isoformat() 12 | else: 13 | raise TypeError 14 | 15 | 16 | class Command(BaseCommand): 17 | help = 'Export transmission torrents and what torrents' 18 | 19 | def handle(self, *args, **options): 20 | output = open('what_manager2_torrents.jsonl', 'wb') 21 | what_replica = ReplicaSet.objects.get(zone='what.cd', name='master') 22 | counter = 0 23 | for instance in what_replica.transinstance_set.all(): 24 | print 'Process instance', instance.name 25 | t_torrents = instance.transtorrent_set.all().select_related('what_torrent', 'location') 26 | for t_torrent in t_torrents: 27 | output.write(json.dumps({ 28 | 'trans_torrent': model_to_dict(t_torrent), 29 | 'what_torrent': model_to_dict(t_torrent.what_torrent), 30 | 'location': model_to_dict(t_torrent.location), 31 | }, default=date_handler)) 32 | output.write('\n') 33 | counter += 1 34 | if counter % 1000 == 0: 35 | print 'Done', counter 36 | print 'Total done:', counter 37 | -------------------------------------------------------------------------------- /wcd_pth_migration/management/commands/init_torrent_group_mapping.py: -------------------------------------------------------------------------------- 1 | import ujson 2 | 3 | from django.core.management.base import BaseCommand 4 | 5 | from home.models import get_what_client, BadIdException 6 | from wcd_pth_migration.models import WhatTorrentMigrationStatus, TorrentGroupMapping 7 | 8 | 9 | class Command(BaseCommand): 10 | help = 'Saves the current complete uploads as WhatTorrentGroupMatching objects' 11 | 12 | def handle(self, *args, **options): 13 | what = get_what_client(lambda: None, True) 14 | with open('what_manager2_torrents.jsonl', 'rb') as torrents_input: 15 | for line in torrents_input: 16 | data = ujson.loads(line) 17 | info = ujson.loads(data['what_torrent']['info']) 18 | what_torrent_id = info['torrent']['id'] 19 | what_group_id = info['group']['id'] 20 | try: 21 | TorrentGroupMapping.objects.get(what_group_id=what_group_id) 22 | continue 23 | except TorrentGroupMapping.DoesNotExist: 24 | pass 25 | try: 26 | migration_status = WhatTorrentMigrationStatus.objects.get( 27 | what_torrent_id=what_torrent_id) 28 | except WhatTorrentMigrationStatus.DoesNotExist: 29 | continue 30 | if migration_status.status != WhatTorrentMigrationStatus.STATUS_COMPLETE: 31 | continue 32 | pth_torrent_id = migration_status.pth_torrent_id 33 | if not pth_torrent_id: 34 | continue 35 | try: 36 | pth_torrent = what.request('torrent', id=pth_torrent_id)['response'] 37 | except BadIdException: 38 | continue 39 | pth_group_id = pth_torrent['group']['id'] 40 | print 'Saving {} mapping to {}'.format(what_group_id, pth_group_id) 41 | TorrentGroupMapping.objects.create( 42 | what_group_id=what_group_id, 43 | pth_group_id=pth_group_id, 44 | ) 45 | -------------------------------------------------------------------------------- /wcd_pth_migration/management/commands/migration_clear_whatmanager.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from books.models import BookUpload 4 | from home.models import WhatTorrent, TransTorrent, WhatFulltext, WhatFileMetadataCache, \ 5 | WhatLoginCache, ReplicaSet 6 | from what_transcode.models import TranscodeRequest 7 | 8 | 9 | class Command(BaseCommand): 10 | help = 'Clears the database of what torrents, trans torrents, also removes all torrents from transmission' 11 | 12 | def handle(self, *args, **options): 13 | print 'Deleting BookUpload...' 14 | BookUpload.objects.all().delete() 15 | print 'Deleting TranscodeRequest...' 16 | TranscodeRequest.objects.all().delete() 17 | print 'Deleting WhatLoginCache...' 18 | WhatLoginCache.objects.all().delete() 19 | print 'Deleting WhatFileMetadataCache...' 20 | WhatFileMetadataCache.objects.all().delete() 21 | print 'Deleting WhatFulltext...' 22 | WhatFulltext.objects.all().delete() 23 | print 'Deleting TransTorrent...' 24 | TransTorrent.objects.all().delete() 25 | print 'Deleting WhatTorrent...' 26 | WhatTorrent.objects.all().delete() 27 | # Delete all torrents 28 | for instance in ReplicaSet.objects.get(zone='what.cd').transinstance_set.all(): 29 | print 'Fetching torrents from', instance.name 30 | torrent_ids = [t.id for t in instance.client.get_torrents(arguments=['id'])] 31 | print 'Removing torrents' 32 | instance.client.remove_torrent(torrent_ids) 33 | -------------------------------------------------------------------------------- /wcd_pth_migration/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='DownloadLocationEquivalent', 15 | fields=[ 16 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 17 | ('old_location', models.CharField(max_length=512)), 18 | ('new_location', models.CharField(max_length=512)), 19 | ], 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /wcd_pth_migration/migrations/0002_whattorrentmigrationstatus.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('wcd_pth_migration', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='WhatTorrentMigrationStatus', 16 | fields=[ 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 18 | ('what_torrent_id', models.BigIntegerField()), 19 | ('status', models.IntegerField()), 20 | ], 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /wcd_pth_migration/migrations/0003_auto_20161206_1055.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('wcd_pth_migration', '0002_whattorrentmigrationstatus'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='whattorrentmigrationstatus', 16 | name='what_torrent_id', 17 | field=models.BigIntegerField(unique=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /wcd_pth_migration/migrations/0004_whattorrentmigrationstatus_pth_torrent_id.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('wcd_pth_migration', '0003_auto_20161206_1055'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='whattorrentmigrationstatus', 16 | name='pth_torrent_id', 17 | field=models.BigIntegerField(null=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /wcd_pth_migration/migrations/0005_auto_20161216_1701.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('wcd_pth_migration', '0004_whattorrentmigrationstatus_pth_torrent_id'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='TorrentGroupMapping', 16 | fields=[ 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 18 | ('what_group_id', models.BigIntegerField()), 19 | ('pth_group_id', models.BigIntegerField()), 20 | ], 21 | ), 22 | migrations.AlterUniqueTogether( 23 | name='torrentgroupmapping', 24 | unique_together=set([('what_group_id', 'pth_group_id')]), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /wcd_pth_migration/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/wcd_pth_migration/migrations/__init__.py -------------------------------------------------------------------------------- /wcd_pth_migration/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class DownloadLocationEquivalent(models.Model): 5 | old_location = models.CharField(max_length=512) 6 | new_location = models.CharField(max_length=512) 7 | 8 | 9 | class WhatTorrentMigrationStatus(models.Model): 10 | STATUS_PROCESSING = 0 11 | STATUS_DUPLICATE = 1 12 | STATUS_SKIPPED = 2 13 | STATUS_UPLOADED = 3 14 | STATUS_COMPLETE = 4 15 | STATUS_SKIPPED_PERMANENTLY = 5 16 | STATUS_FAILED_VALIDATION = 6 17 | STATUS_RESEEDED = 7 18 | 19 | what_torrent_id = models.BigIntegerField(unique=True) 20 | status = models.IntegerField() 21 | pth_torrent_id = models.BigIntegerField(null=True) 22 | 23 | 24 | class TorrentGroupMapping(models.Model): 25 | what_group_id = models.BigIntegerField() 26 | pth_group_id = models.BigIntegerField() 27 | 28 | class Meta: 29 | unique_together = (('what_group_id', 'pth_group_id'),) 30 | -------------------------------------------------------------------------------- /wcd_pth_migration/test.py: -------------------------------------------------------------------------------- 1 | f = open('/Users/ivailo/Downloads/test_log.log', 'r') 2 | d = f.read() 3 | 4 | from logfile import * 5 | l = LogFile(d) 6 | print l.toc 7 | print 8 | print l.tracks 9 | -------------------------------------------------------------------------------- /wcd_pth_migration/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /wcd_pth_migration/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /what_json/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_json/__init__.py -------------------------------------------------------------------------------- /what_json/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_json/models.py -------------------------------------------------------------------------------- /what_json/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /what_json/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns( 4 | '', 5 | url(r'^checks$', 'what_json.views.checks'), 6 | url(r'^add_torrent$', 'what_json.views.add_torrent'), 7 | url(r'^sync$', 'what_json.views.sync'), 8 | url(r'^sync_replicas$', 'what_json.views.sync_replicas'), 9 | url(r'^update_freeleech$', 'what_json.views.update_freeleech'), 10 | url(r'^load_balance$', 'what_json.views.run_load_balance'), 11 | url(r'^move_torrent$', 'what_json.views.move_torrent_to_location'), 12 | url(r'^torrents_info$', 'what_json.views.torrents_info'), 13 | url(r'^refresh_whattorrent$', 'what_json.views.refresh_whattorrent'), 14 | url(r'^what_proxy$', 'what_json.views.what_proxy'), 15 | ) 16 | -------------------------------------------------------------------------------- /what_json/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.utils import timezone 4 | 5 | from home.models import RequestException, TransTorrent, ReplicaSet, WhatTorrent 6 | 7 | 8 | def refresh_whattorrent(what_client, what_torrent=None): 9 | if what_torrent is None: 10 | what_torrent = WhatTorrent.objects.defer('torrent_file').order_by('retrieved')[0] 11 | 12 | try: 13 | response = what_client.request('torrent', id=what_torrent.id)['response'] 14 | except RequestException as ex: 15 | if ex.response and type(ex.response) is dict and ex.response.get( 16 | 'error') == 'bad id parameter': 17 | try: 18 | TransTorrent.objects.get( 19 | instance__in=ReplicaSet.get_what_master().transinstance_set.all(), 20 | what_torrent=what_torrent) 21 | return { 22 | 'success': False, 23 | 'id': what_torrent.id, 24 | 'status': 'missing', 25 | } 26 | except TransTorrent.DoesNotExist: 27 | what_torrent.delete() 28 | return { 29 | 'success': True, 30 | 'id': what_torrent.id, 31 | 'status': 'deleted', 32 | } 33 | else: 34 | return { 35 | 'success': False, 36 | 'status': 'unknown request exception', 37 | } 38 | 39 | old_retrieved = what_torrent.retrieved 40 | what_torrent.info = json.dumps(response) 41 | what_torrent.retrieved = timezone.now() 42 | what_torrent.save() 43 | return { 44 | 'success': True, 45 | 'id': what_torrent.id, 46 | 'status': 'refreshed', 47 | 'retrieved': unicode(old_retrieved), 48 | } 49 | -------------------------------------------------------------------------------- /what_meta/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_meta/__init__.py -------------------------------------------------------------------------------- /what_meta/admin.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_meta/admin.py -------------------------------------------------------------------------------- /what_meta/migrations/0002_whatmetafulltext.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('what_meta', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='WhatMetaFulltext', 16 | fields=[ 17 | ('id', models.AutoField( 18 | verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 19 | ('info', models.TextField()), 20 | ('more_info', models.TextField()), 21 | ('artist', models.ForeignKey(null=True, to='what_meta.WhatArtist', unique=True)), 22 | ('torrent_group', models.ForeignKey( 23 | null=True, to='what_meta.WhatTorrentGroup', unique=True)), 24 | ], 25 | options={ 26 | }, 27 | bases=(models.Model,), 28 | ), 29 | 30 | # Add indexes and fix fulltext 31 | migrations.RunSQL( 32 | 'ALTER TABLE `what_meta_whatmetafulltext` ENGINE = MYISAM', 33 | 'ALTER TABLE `what_meta_whatmetafulltext` ENGINE = INNODB', 34 | ), 35 | migrations.RunSQL( 36 | 'ALTER TABLE `what_meta_whatmetafulltext` ADD FULLTEXT `info_fts` (`info`)', 37 | 'ALTER TABLE `what_meta_whatmetafulltext` DROP INDEX `info_fts`', 38 | ), 39 | migrations.RunSQL( 40 | 'ALTER TABLE `what_meta_whatmetafulltext` ADD ' + 41 | 'FULLTEXT `info_more_info_fts` (`info`,`more_info`)', 42 | 'ALTER TABLE `what_meta_whatmetafulltext` DROP INDEX `info_more_info_fts`' 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /what_meta/migrations/0003_add_artist_aliases.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ('what_meta', '0002_whatmetafulltext'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='WhatArtistAlias', 15 | fields=[ 16 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, 17 | primary_key=True)), 18 | ('name', models.CharField(unique=True, max_length=200)), 19 | ('artist', models.ForeignKey(to='what_meta.WhatArtist')), 20 | ], 21 | options={ 22 | }, 23 | bases=(models.Model,), 24 | ), 25 | migrations.AddField( 26 | model_name='whatmetafulltext', 27 | name='artist_alias', 28 | field=models.ForeignKey(null=True, to='what_meta.WhatArtistAlias', unique=True), 29 | preserve_default=True, 30 | ), 31 | migrations.AddField( 32 | model_name='whattorrentartist', 33 | name='artist_alias', 34 | field=models.ForeignKey(to='what_meta.WhatArtistAlias', null=True), 35 | preserve_default=True, 36 | ), 37 | migrations.AlterField( 38 | model_name='whatartist', 39 | name='name', 40 | field=models.CharField(max_length=200, db_index=True), 41 | ), 42 | migrations.AlterField( 43 | model_name='whatartist', 44 | name='retrieved', 45 | field=models.DateTimeField(db_index=True), 46 | ), 47 | migrations.RunSQL( 48 | 'ALTER TABLE `what_meta_whattorrentgroup` ADD INDEX `name_index` (`name` (200))', 49 | 'ALTER TABLE `what_meta_whattorrentgroup` DROP INDEX `name_index`', 50 | ), 51 | ] 52 | -------------------------------------------------------------------------------- /what_meta/migrations/0004_one_to_one_field.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('what_meta', '0003_add_artist_aliases'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='whatmetafulltext', 16 | name='artist', 17 | field=models.OneToOneField(null=True, to='what_meta.WhatArtist'), 18 | ), 19 | migrations.AlterField( 20 | model_name='whatmetafulltext', 21 | name='artist_alias', 22 | field=models.OneToOneField(null=True, to='what_meta.WhatArtistAlias'), 23 | ), 24 | migrations.AlterField( 25 | model_name='whatmetafulltext', 26 | name='torrent_group', 27 | field=models.OneToOneField(null=True, to='what_meta.WhatTorrentGroup'), 28 | ), 29 | ] -------------------------------------------------------------------------------- /what_meta/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_meta/migrations/__init__.py -------------------------------------------------------------------------------- /what_meta/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns( 4 | '', 5 | url(r'^cached_image$', 'what_meta.views.image'), 6 | ) 7 | -------------------------------------------------------------------------------- /what_meta/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import urllib 4 | import datetime 5 | import time 6 | 7 | from django.contrib.auth.decorators import login_required 8 | from django.http.response import HttpResponse, HttpResponseNotFound 9 | from django.utils import timezone 10 | from django.utils.http import parse_http_date_safe, http_date 11 | from django.views.decorators.http import last_modified 12 | import requests 13 | from requests.exceptions import ConnectionError 14 | 15 | from WhatManager2.settings import MEDIA_ROOT 16 | 17 | 18 | def get_image_cache_path(url): 19 | return os.path.join(MEDIA_ROOT, 'what_image_cache', urllib.quote(url, '')) 20 | 21 | 22 | def get_image_last_modified(request): 23 | url = request.GET.get('url') 24 | if not url: 25 | return None 26 | image_path = get_image_cache_path(url) 27 | try: 28 | s = os.path.getmtime(image_path) 29 | return datetime.datetime.utcfromtimestamp(s) 30 | except OSError: 31 | return None 32 | 33 | 34 | @login_required 35 | @last_modified(get_image_last_modified) 36 | def image(request): 37 | url = request.GET.get('url') 38 | if not url: 39 | return HttpResponseNotFound('Provide a url get param') 40 | image_path = get_image_cache_path(url) 41 | if os.path.isfile(image_path): 42 | image_file = open(image_path, 'rb') 43 | return HttpResponse(image_file, content_type='image/' + os.path.splitext(image_path)[1][1:]) 44 | 45 | try: 46 | response = requests.get(url) 47 | except ConnectionError: 48 | return HttpResponseNotFound('Error connecting to image host') 49 | if response.status_code != 200: 50 | return HttpResponseNotFound('Not Found') 51 | if 'Last-Modified' in response.headers: 52 | modified = parse_http_date_safe(response.headers['Last-Modified']) 53 | else: 54 | modified = time.mktime(timezone.now().utctimetuple()) 55 | with open(image_path, 'wb') as image_file: 56 | image_file.write(response.content) 57 | os.utime(image_path, (int(modified), int(modified))) 58 | response = HttpResponse(response.content, content_type=response.headers['Content-Type']) 59 | response['Last-Modified'] = http_date(modified) 60 | return response 61 | -------------------------------------------------------------------------------- /what_profile/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_profile/__init__.py -------------------------------------------------------------------------------- /what_profile/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name='WhatUserSnapshot', 14 | fields=[ 15 | ('id', models.AutoField(verbose_name='ID', serialize=False, 16 | auto_created=True, primary_key=True)), 17 | ('user_id', models.IntegerField()), 18 | ('datetime', models.DateTimeField(auto_now_add=True)), 19 | ('uploaded', models.BigIntegerField()), 20 | ('downloaded', models.BigIntegerField()), 21 | ('info', models.TextField()), 22 | ], 23 | options={ 24 | 'permissions': (('view_whatusersnapshot', 'Can view the user profile.'),), 25 | }, 26 | bases=(models.Model,), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /what_profile/migrations/0002_add_datetime_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('what_profile', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='whatusersnapshot', 16 | name='datetime', 17 | field=models.DateTimeField(auto_now_add=True, db_index=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /what_profile/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_profile/migrations/__init__.py -------------------------------------------------------------------------------- /what_profile/models.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.db import models 4 | from django.utils import timezone 5 | from django.utils.functional import cached_property 6 | 7 | 8 | class WhatUserSnapshot(models.Model): 9 | class Meta: 10 | permissions = ( 11 | ('view_whatusersnapshot', 'Can view the user profile.'), 12 | ) 13 | 14 | user_id = models.IntegerField() 15 | datetime = models.DateTimeField(auto_now_add=True, db_index=True) 16 | uploaded = models.BigIntegerField() 17 | downloaded = models.BigIntegerField() 18 | info = models.TextField() 19 | 20 | @cached_property 21 | def buffer_105(self): 22 | return self.uploaded / 1.05 - self.downloaded 23 | 24 | @cached_property 25 | def ratio(self): 26 | return float(self.uploaded) / self.downloaded 27 | 28 | @classmethod 29 | def get(cls, what_client, user_id): 30 | data = what_client.request('user', id=user_id)['response'] 31 | return WhatUserSnapshot( 32 | user_id=user_id, 33 | uploaded=int(data['stats']['uploaded']), 34 | downloaded=int(data['stats']['downloaded']), 35 | info=json.dumps(data), 36 | ) 37 | 38 | @classmethod 39 | def get_last(cls): 40 | snapshots = WhatUserSnapshot.objects.order_by('-datetime')[:1] 41 | if len(snapshots): 42 | return snapshots[0] 43 | raise WhatUserSnapshot.DoesNotExist() 44 | 45 | @classmethod 46 | def get_closest_snapshot(self, when): 47 | snapshots = WhatUserSnapshot.objects.extra(select={ 48 | 'delta': 'ABS(TIMESTAMPDIFF(SECOND, datetime, %s))' 49 | }, order_by=['delta'], select_params=[when])[:1] 50 | if len(snapshots): 51 | return snapshots[0] 52 | raise WhatUserSnapshot.DoesNotExist() 53 | 54 | @classmethod 55 | def buffer_delta(cls, delta): 56 | old_snapshot = cls.get_closest_snapshot(timezone.now() - delta) 57 | new_snapshot = cls.get_last() 58 | return new_snapshot.buffer_105 - old_snapshot.buffer_105 59 | -------------------------------------------------------------------------------- /what_profile/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /what_profile/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns( 4 | '', 5 | url(r'^$', 'what_profile.views.profile'), 6 | url(r'^part/buffer_up_down_data$', 'what_profile.parts.buffer_up_down_data'), 7 | url(r'^part/profile_history', 'what_profile.parts.profile_history'), 8 | ) 9 | -------------------------------------------------------------------------------- /what_profile/views.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from django.contrib.auth.decorators import login_required 4 | from django.shortcuts import render 5 | 6 | from what_profile.models import WhatUserSnapshot 7 | 8 | 9 | @login_required 10 | def profile(request): 11 | try: 12 | data = { 13 | 'delta_hour': WhatUserSnapshot.buffer_delta(datetime.timedelta(hours=1)), 14 | 'delta_day': WhatUserSnapshot.buffer_delta(datetime.timedelta(days=1)), 15 | 'delta_week': WhatUserSnapshot.buffer_delta(datetime.timedelta(days=7)), 16 | 'delta_month': WhatUserSnapshot.buffer_delta(datetime.timedelta(days=30)), 17 | 'buffer': WhatUserSnapshot.get_last().buffer_105, 18 | } 19 | except WhatUserSnapshot.DoesNotExist: 20 | data = { 21 | 'delta_hour': '-', 22 | 'delta_day': '-', 23 | 'delta_week': '-', 24 | 'delta_month': '-', 25 | 'buffer': '-', 26 | } 27 | 28 | return render(request, 'what_profile/profile.html', data) 29 | -------------------------------------------------------------------------------- /what_transcode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_transcode/__init__.py -------------------------------------------------------------------------------- /what_transcode/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ('home', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='TranscodeRequest', 15 | fields=[ 16 | ('id', models.AutoField(verbose_name='ID', serialize=False, 17 | auto_created=True, primary_key=True)), 18 | ('requested_by_ip', models.TextField()), 19 | ('requested_by_what_user', models.TextField()), 20 | ('date_requested', models.DateTimeField(auto_now_add=True)), 21 | ('date_completed', models.DateTimeField(null=True)), 22 | ('celery_task_id', models.TextField(null=True)), 23 | ('what_torrent', models.ForeignKey(to='home.WhatTorrent')), 24 | ], 25 | options={ 26 | }, 27 | bases=(models.Model,), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /what_transcode/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/what_transcode/migrations/__init__.py -------------------------------------------------------------------------------- /what_transcode/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | from home.models import WhatTorrent 5 | 6 | 7 | class TranscodeRequest(models.Model): 8 | what_torrent = models.ForeignKey(WhatTorrent) 9 | requested_by_ip = models.TextField() 10 | requested_by_what_user = models.TextField() 11 | date_requested = models.DateTimeField(auto_now_add=True) 12 | date_completed = models.DateTimeField(null=True) 13 | celery_task_id = models.TextField(null=True) 14 | -------------------------------------------------------------------------------- /what_transcode/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns( 4 | '', 5 | url(r'^$', 'what_transcode.views.index'), 6 | url(r'^status_table$', 'what_transcode.views.status_table'), 7 | url(r'^request$', 'what_transcode.views.request_transcode'), 8 | url(r'^request_retry$', 'what_transcode.views.request_retry'), 9 | url(r'^update$', 'what_transcode.views.update'), 10 | ) 11 | -------------------------------------------------------------------------------- /whatify/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/whatify/__init__.py -------------------------------------------------------------------------------- /whatify/admin.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/whatify/admin.py -------------------------------------------------------------------------------- /whatify/filtering.py: -------------------------------------------------------------------------------- 1 | from home.models import WhatTorrent 2 | 3 | 4 | def extract(torrent): 5 | t = type(torrent) 6 | if t is dict: 7 | return torrent['media'], torrent['format'] 8 | elif t is WhatTorrent: 9 | return torrent.info_media, torrent.info_format 10 | 11 | 12 | # This should be changed when we support FLAC 13 | def filter_torrent(a): 14 | _, a_format = extract(a) 15 | if a_format == 'FLAC': 16 | return True 17 | return True 18 | 19 | 20 | def compare_torrents(a, b): 21 | a_media, a_format = extract(a) 22 | b_media, b_format = extract(b) 23 | if a_format == 'MP3' and b_format != 'MP3': 24 | return -1 25 | if a_format != 'MP3' and b_format == 'MP3': 26 | return 1 27 | if a_media == 'WEB' and b_media != 'WEB': 28 | return -1 29 | if a_media != 'WEB' and b_media == 'WEB': 30 | return 1 31 | if a_media == 'CD' and b_media != 'CD': 32 | return -1 33 | if a_media != 'CD' and b_media == 'CD': 34 | return 1 35 | return 0 36 | 37 | 38 | def sort_filter_torrents(torrents): 39 | torrents = [t for t in torrents if filter_torrent(t)] 40 | torrents.sort(cmp=compare_torrents) 41 | return torrents 42 | -------------------------------------------------------------------------------- /whatify/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/whatify/migrations/__init__.py -------------------------------------------------------------------------------- /whatify/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/whatify/models.py -------------------------------------------------------------------------------- /whatify/tests.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karamanolev/WhatManager2/ddbce0fa1ff4e1fc44bfa726c4f7eace4adbe8a9/whatify/tests.py -------------------------------------------------------------------------------- /whatify/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns( 4 | '', 5 | url(r'^$', 'whatify.views.index'), 6 | url(r'^search/(.+)$', 'whatify.views.search'), 7 | url(r'^torrent_groups/(\d+)$', 'whatify.views.get_torrent_group'), 8 | url(r'^torrent_groups/(\d+)/download$', 'whatify.views.download_torrent_group'), 9 | url(r'^torrent_groups/random$', 'whatify.views.random_torrent_groups'), 10 | url(r'^torrent_groups/top10$', 'whatify.views.top10_torrent_groups'), 11 | url(r'^artists/(\d+)$', 'whatify.views.get_artist'), 12 | ) 13 | -------------------------------------------------------------------------------- /whatify/utils.py: -------------------------------------------------------------------------------- 1 | def extended_artists_to_music_info(extended_artists): 2 | return { 3 | 'musicInfo': { 4 | 'artists': extended_artists['1'] or [], 5 | 'with': extended_artists['2'] or [], 6 | 'remixedBy': extended_artists['3'] or [], 7 | 'composers': extended_artists['4'] or [], 8 | 'conductor': extended_artists['5'] or [], 9 | 'dj': extended_artists['6'] or [], 10 | 'producer': extended_artists['7'] or [], 11 | } 12 | } 13 | 14 | 15 | # Add some preferences here 16 | def get_ids_to_download(torrent_group): 17 | ids = [] 18 | for torrent in torrent_group.torrents: 19 | if torrent['format'].lower() != 'flac': 20 | continue 21 | if torrent['media'].lower() not in ['cd', 'web']: 22 | continue 23 | ids.append(torrent['id']) 24 | return ids 25 | --------------------------------------------------------------------------------