Implemented YouTube layer, as well as synchronization on schedule. TODO: fix issue where ready() is called twice. The synchronization should only run on one thread/process.
This commit is contained in:
parent
c26732d101
commit
291da16461
11
.idea/dataSources.local.xml
Normal file
11
.idea/dataSources.local.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal">
|
||||
<data-source name="Django default" uuid="2dac2136-d902-4d27-8789-9371934602fd">
|
||||
<database-info product="SQLite" version="3.20.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.20.1.1" family="SQLITE" exact-version="3.20.1" />
|
||||
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||
<auth-required>false</auth-required>
|
||||
<introspection-schemas>*:@</introspection-schemas>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
16
.idea/dataSources.xml
Normal file
16
.idea/dataSources.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="Django default" uuid="2dac2136-d902-4d27-8789-9371934602fd">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<imported>true</imported>
|
||||
<remarks>$PROJECT_DIR$/YtManager/settings.py</remarks>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:D:\Dev\youtube-channel-manager\db.sqlite3</jdbc-url>
|
||||
<driver-properties>
|
||||
<property name="enable_load_extension" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
741
.idea/dataSources/2dac2136-d902-4d27-8789-9371934602fd.xml
Normal file
741
.idea/dataSources/2dac2136-d902-4d27-8789-9371934602fd.xml
Normal file
@ -0,0 +1,741 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<dataSource name="Django default">
|
||||
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.10">
|
||||
<root id="1">
|
||||
<ServerVersion>3.20.1</ServerVersion>
|
||||
</root>
|
||||
<schema id="2" parent="1" name="main">
|
||||
<Current>1</Current>
|
||||
<Visible>1</Visible>
|
||||
</schema>
|
||||
<collation id="3" parent="1" name="BINARY"/>
|
||||
<collation id="4" parent="1" name="NOCASE"/>
|
||||
<collation id="5" parent="1" name="RTRIM"/>
|
||||
<table id="6" parent="2" name="YtManagerApp_channel"/>
|
||||
<table id="7" parent="2" name="YtManagerApp_subscription"/>
|
||||
<table id="8" parent="2" name="YtManagerApp_subscriptionfolder"/>
|
||||
<table id="9" parent="2" name="YtManagerApp_video"/>
|
||||
<table id="10" parent="2" name="auth_group"/>
|
||||
<table id="11" parent="2" name="auth_group_permissions"/>
|
||||
<table id="12" parent="2" name="auth_permission"/>
|
||||
<table id="13" parent="2" name="auth_user"/>
|
||||
<table id="14" parent="2" name="auth_user_groups"/>
|
||||
<table id="15" parent="2" name="auth_user_user_permissions"/>
|
||||
<table id="16" parent="2" name="django_admin_log"/>
|
||||
<table id="17" parent="2" name="django_content_type"/>
|
||||
<table id="18" parent="2" name="django_migrations"/>
|
||||
<table id="19" parent="2" name="django_session"/>
|
||||
<table id="20" parent="2" name="sqlite_master">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<table id="21" parent="2" name="sqlite_sequence">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<column id="22" parent="6" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="23" parent="6" name="channel_id">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="24" parent="6" name="username">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="25" parent="6" name="custom_url">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="26" parent="6" name="name">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="27" parent="6" name="description">
|
||||
<Position>6</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="28" parent="6" name="icon_default">
|
||||
<Position>7</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="29" parent="6" name="icon_best">
|
||||
<Position>8</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="30" parent="6" name="upload_playlist_id">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="31" parent="6" name="sqlite_autoindex_YtManagerApp_channel_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>channel_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="32" parent="6" name="sqlite_autoindex_YtManagerApp_channel_2">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>username</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="33" parent="6" name="sqlite_autoindex_YtManagerApp_channel_3">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>custom_url</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="34" parent="6">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="35" parent="6">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<key id="36" parent="6">
|
||||
<ColNames>username</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_2</UnderlyingIndexName>
|
||||
</key>
|
||||
<key id="37" parent="6">
|
||||
<ColNames>custom_url</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_3</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="38" parent="7" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="39" parent="7" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="40" parent="7" name="parent_folder_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="41" parent="7" name="playlist_id">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="42" parent="7" name="description">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="43" parent="7" name="icon_best">
|
||||
<Position>6</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="44" parent="7" name="icon_default">
|
||||
<Position>7</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="45" parent="7" name="channel_id">
|
||||
<Position>8</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="46" parent="7" name="sqlite_autoindex_YtManagerApp_subscription_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>playlist_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="47" parent="7" name="YtManagerApp_subscription_parent_folder_id_c4c64c21">
|
||||
<ColNames>parent_folder_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="48" parent="7" name="YtManagerApp_subscription_channel_id_b83c6f21">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="49" parent="7">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="50" parent="7">
|
||||
<ColNames>playlist_id</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_subscription_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<foreign-key id="51" parent="7">
|
||||
<ColNames>parent_folder_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="52" parent="7">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<RefTableName>YtManagerApp_channel</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="53" parent="8" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="54" parent="8" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="55" parent="8" name="parent_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<index id="56" parent="8" name="YtManagerApp_subscriptionfolder_parent_id_bd5f4bc1">
|
||||
<ColNames>parent_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="57" parent="8">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="58" parent="8">
|
||||
<ColNames>parent_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="59" parent="9" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="60" parent="9" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="61" parent="9" name="video_id">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="62" parent="9" name="downloaded_path">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="63" parent="9" name="watched">
|
||||
<Position>5</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="64" parent="9" name="subscription_id">
|
||||
<Position>6</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="65" parent="9" name="description">
|
||||
<Position>7</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="66" parent="9" name="icon_best">
|
||||
<Position>8</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="67" parent="9" name="icon_default">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="68" parent="9" name="playlist_index">
|
||||
<Position>10</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="69" parent="9" name="publish_date">
|
||||
<Position>11</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="70" parent="9" name="YtManagerApp_video_subscription_id_720d4227">
|
||||
<ColNames>subscription_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="71" parent="9">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="72" parent="9">
|
||||
<ColNames>subscription_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscription</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="73" parent="10" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="74" parent="10" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(80)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="75" parent="10" name="sqlite_autoindex_auth_group_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>name</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="76" parent="10">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="77" parent="10">
|
||||
<ColNames>name</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_auth_group_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="78" parent="11" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="79" parent="11" name="group_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="80" parent="11" name="permission_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="81" parent="11" name="auth_group_permissions_group_id_permission_id_0cd325b0_uniq">
|
||||
<ColNames>group_id
|
||||
permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="82" parent="11" name="auth_group_permissions_group_id_b120cbf9">
|
||||
<ColNames>group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="83" parent="11" name="auth_group_permissions_permission_id_84c5c92e">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="84" parent="11">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="85" parent="11">
|
||||
<ColNames>group_id</ColNames>
|
||||
<RefTableName>auth_group</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="86" parent="11">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<RefTableName>auth_permission</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="87" parent="12" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="88" parent="12" name="content_type_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="89" parent="12" name="codename">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="90" parent="12" name="name">
|
||||
<Position>4</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="91" parent="12" name="auth_permission_content_type_id_codename_01ab375a_uniq">
|
||||
<ColNames>content_type_id
|
||||
codename</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="92" parent="12" name="auth_permission_content_type_id_2f476e4b">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="93" parent="12">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="94" parent="12">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<RefTableName>django_content_type</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="95" parent="13" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="96" parent="13" name="password">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(128)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="97" parent="13" name="last_login">
|
||||
<Position>3</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
</column>
|
||||
<column id="98" parent="13" name="is_superuser">
|
||||
<Position>4</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="99" parent="13" name="username">
|
||||
<Position>5</Position>
|
||||
<DataType>varchar(150)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="100" parent="13" name="first_name">
|
||||
<Position>6</Position>
|
||||
<DataType>varchar(30)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="101" parent="13" name="email">
|
||||
<Position>7</Position>
|
||||
<DataType>varchar(254)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="102" parent="13" name="is_staff">
|
||||
<Position>8</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="103" parent="13" name="is_active">
|
||||
<Position>9</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="104" parent="13" name="date_joined">
|
||||
<Position>10</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="105" parent="13" name="last_name">
|
||||
<Position>11</Position>
|
||||
<DataType>varchar(150)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="106" parent="13" name="sqlite_autoindex_auth_user_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>username</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="107" parent="13">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="108" parent="13">
|
||||
<ColNames>username</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_auth_user_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="109" parent="14" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="110" parent="14" name="user_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="111" parent="14" name="group_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="112" parent="14" name="auth_user_groups_user_id_group_id_94350c0c_uniq">
|
||||
<ColNames>user_id
|
||||
group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="113" parent="14" name="auth_user_groups_user_id_6a12ed8b">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="114" parent="14" name="auth_user_groups_group_id_97559544">
|
||||
<ColNames>group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="115" parent="14">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="116" parent="14">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="117" parent="14">
|
||||
<ColNames>group_id</ColNames>
|
||||
<RefTableName>auth_group</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="118" parent="15" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="119" parent="15" name="user_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="120" parent="15" name="permission_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="121" parent="15" name="auth_user_user_permissions_user_id_permission_id_14a6b632_uniq">
|
||||
<ColNames>user_id
|
||||
permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="122" parent="15" name="auth_user_user_permissions_user_id_a95ead1b">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="123" parent="15" name="auth_user_user_permissions_permission_id_1fbb5f2c">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="124" parent="15">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="125" parent="15">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="126" parent="15">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<RefTableName>auth_permission</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="127" parent="16" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="128" parent="16" name="action_time">
|
||||
<Position>2</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="129" parent="16" name="object_id">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="130" parent="16" name="object_repr">
|
||||
<Position>4</Position>
|
||||
<DataType>varchar(200)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="131" parent="16" name="change_message">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="132" parent="16" name="content_type_id">
|
||||
<Position>6</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="133" parent="16" name="user_id">
|
||||
<Position>7</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="134" parent="16" name="action_flag">
|
||||
<Position>8</Position>
|
||||
<DataType>smallint unsigned|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="135" parent="16" name="django_admin_log_content_type_id_c4bce8eb">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="136" parent="16" name="django_admin_log_user_id_c564eba6">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="137" parent="16">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="138" parent="16">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<RefTableName>django_content_type</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="139" parent="16">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="140" parent="17" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="141" parent="17" name="app_label">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="142" parent="17" name="model">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="143" parent="17" name="django_content_type_app_label_model_76bd3d3b_uniq">
|
||||
<ColNames>app_label
|
||||
model</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="144" parent="17">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<column id="145" parent="18" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="146" parent="18" name="app">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="147" parent="18" name="name">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="148" parent="18" name="applied">
|
||||
<Position>4</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<key id="149" parent="18">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<column id="150" parent="19" name="session_key">
|
||||
<Position>1</Position>
|
||||
<DataType>varchar(40)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="151" parent="19" name="session_data">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="152" parent="19" name="expire_date">
|
||||
<Position>3</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="153" parent="19" name="sqlite_autoindex_django_session_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>session_key</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="154" parent="19" name="django_session_expire_date_a5c62663">
|
||||
<ColNames>expire_date</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="155" parent="19">
|
||||
<ColNames>session_key</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_django_session_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="156" parent="20" name="type">
|
||||
<Position>1</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="157" parent="20" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="158" parent="20" name="tbl_name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="159" parent="20" name="rootpage">
|
||||
<Position>4</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="160" parent="20" name="sql">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="161" parent="21" name="name">
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="162" parent="21" name="seq">
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
</database-model>
|
||||
</dataSource>
|
@ -0,0 +1,2 @@
|
||||
#n:main
|
||||
!<md> [0, 0, null, null, -2147483648, -2147483648]
|
@ -2,20 +2,21 @@
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="245751b6-c863-4572-8723-8499964fe105" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/watcherTasks.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/management.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/migrations/0004_auto_20181005_1626.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.css.map" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/dataSources.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/migrations/0005_auto_20181007_2015.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/migrations/0006_auto_20181008_0037.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/youtube.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManager/settings.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManager/settings.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManager/urls.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManager/urls.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.css" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/import/jstree/jstree.js" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/import/jstree/jstree.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/js/subtree.js" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/admin.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/admin.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/apps.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/apps.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/management.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/models.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_dialog.html" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_dialog.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/views.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/views.py" afterDir="false" />
|
||||
</list>
|
||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||
@ -24,6 +25,36 @@
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="DatabaseView">
|
||||
<option name="SHOW_INTERMEDIATE" value="true" />
|
||||
<option name="GROUP_DATA_SOURCES" value="true" />
|
||||
<option name="GROUP_SCHEMA" value="true" />
|
||||
<option name="GROUP_CONTENTS" value="false" />
|
||||
<option name="SORT_POSITIONED" value="false" />
|
||||
<option name="SHOW_EMPTY_GROUPS" value="false" />
|
||||
<option name="AUTO_SCROLL_FROM_SOURCE" value="false" />
|
||||
<option name="HIDDEN_KINDS">
|
||||
<set />
|
||||
</option>
|
||||
<expand>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||
<item name="main: schema" type="90513b60:SqliteImplModel$Schema" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</component>
|
||||
<component name="DjangoConsoleOptions" custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)">
|
||||
<option name="myCustomStartScript" value="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)" />
|
||||
</component>
|
||||
@ -31,111 +62,196 @@
|
||||
<session id="1684258556">
|
||||
<usages-collector id="statistics.lifecycle.project">
|
||||
<counts>
|
||||
<entry key="project.closed" value="1" />
|
||||
<entry key="project.closed" value="2" />
|
||||
<entry key="project.open.time.1" value="1" />
|
||||
<entry key="project.open.time.11" value="1" />
|
||||
<entry key="project.open.time.3" value="1" />
|
||||
<entry key="project.opened" value="2" />
|
||||
<entry key="project.opened" value="3" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.edit">
|
||||
<counts>
|
||||
<entry key="Django Console" value="65" />
|
||||
<entry key="css" value="40" />
|
||||
<entry key="html" value="620" />
|
||||
<entry key="js" value="2858" />
|
||||
<entry key="dummy" value="2" />
|
||||
<entry key="html" value="747" />
|
||||
<entry key="js" value="4689" />
|
||||
<entry key="less" value="38" />
|
||||
<entry key="py" value="1710" />
|
||||
<entry key="py@youtube-channel-manager" value="19" />
|
||||
<entry key="py" value="11289" />
|
||||
<entry key="py@youtube-channel-manager" value="95" />
|
||||
<entry key="scss" value="26" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.edit">
|
||||
<counts>
|
||||
<entry key="CSS" value="40" />
|
||||
<entry key="CommandLine" value="19" />
|
||||
<entry key="HTML" value="620" />
|
||||
<entry key="JavaScript" value="2858" />
|
||||
<entry key="CommandLine" value="39" />
|
||||
<entry key="HTML" value="747" />
|
||||
<entry key="JavaScript" value="4689" />
|
||||
<entry key="Less" value="38" />
|
||||
<entry key="Python" value="1710" />
|
||||
<entry key="PLAIN_TEXT" value="58" />
|
||||
<entry key="Python" value="11354" />
|
||||
<entry key="SCSS" value="26" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.open">
|
||||
<counts>
|
||||
<entry key="css" value="1" />
|
||||
<entry key="html" value="8" />
|
||||
<entry key="html" value="17" />
|
||||
<entry key="js" value="5" />
|
||||
<entry key="less" value="1" />
|
||||
<entry key="py" value="7" />
|
||||
<entry key="py" value="26" />
|
||||
<entry key="scss" value="3" />
|
||||
<entry key="ytmanagerapp_channel" value="1" />
|
||||
<entry key="ytmanagerapp_subscription" value="1" />
|
||||
<entry key="ytmanagerapp_subscriptionfolder" value="1" />
|
||||
<entry key="ytmanagerapp_video" value="2" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.open">
|
||||
<counts>
|
||||
<entry key="CSS" value="1" />
|
||||
<entry key="HTML" value="8" />
|
||||
<entry key="Database Element" value="5" />
|
||||
<entry key="HTML" value="17" />
|
||||
<entry key="JavaScript" value="5" />
|
||||
<entry key="Less" value="1" />
|
||||
<entry key="Python" value="7" />
|
||||
<entry key="Python" value="26" />
|
||||
<entry key="SCSS" value="3" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
</session>
|
||||
</component>
|
||||
<component name="FileEditorManager">
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="334">
|
||||
<caret line="62" lean-forward="true" selection-start-line="62" selection-end-line="62" />
|
||||
<folding>
|
||||
<element signature="e#0#35#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="504">
|
||||
<caret line="42" column="11" selection-start-line="42" selection-start-column="11" selection-end-line="42" selection-end-column="11" />
|
||||
<folding>
|
||||
<marker date="1538766618853" expanded="true" signature="90:91" ph="..." />
|
||||
<marker date="1538766618853" expanded="true" signature="642:651" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1700">
|
||||
<caret line="100" column="186" selection-start-line="100" selection-start-column="186" selection-end-line="100" selection-end-column="186" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="357">
|
||||
<caret line="21" column="30" lean-forward="true" selection-start-line="21" selection-start-column="30" selection-end-line="21" selection-end-column="30" />
|
||||
<folding>
|
||||
<element signature="e#433#440#1#HTML" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
<splitter split-orientation="horizontal" split-proportion="0.82295084">
|
||||
<split-first>
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="469">
|
||||
<caret line="87" lean-forward="true" selection-start-line="87" selection-end-line="87" />
|
||||
<folding>
|
||||
<element signature="e#0#35#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManager/urls.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="289">
|
||||
<caret line="17" selection-start-line="17" selection-end-line="17" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="208">
|
||||
<caret line="183" column="47" selection-start-line="183" selection-start-column="47" selection-end-line="183" selection-end-column="47" />
|
||||
<folding>
|
||||
<element signature="e#0#68#0" expanded="true" />
|
||||
<marker date="1538954367974" expanded="true" signature="819:828" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="1597:1603" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="4940:6762" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="5447:5983" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="6067:6076" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="6067:6496" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/__init__.py">
|
||||
<provider selected="true" editor-type-id="text-editor" />
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="438">
|
||||
<caret line="338" column="3" selection-start-line="338" selection-start-column="3" selection-end-line="338" selection-end-column="3" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-first>
|
||||
<split-second>
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_dialog.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="340">
|
||||
<caret line="20" column="29" selection-start-line="20" selection-start-column="29" selection-end-line="20" selection-end-column="29" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="221">
|
||||
<caret line="13" column="33" selection-start-line="13" selection-start-column="33" selection-end-line="13" selection-end-column="33" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="138">
|
||||
<caret line="22" column="45" selection-start-line="22" selection-start-column="45" selection-end-line="22" selection-end-column="45" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/youtube.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="354">
|
||||
<caret line="206" column="24" selection-start-line="206" selection-start-column="24" selection-end-line="206" selection-end-column="24" />
|
||||
<folding>
|
||||
<element signature="e#0#43#0" expanded="true" />
|
||||
<marker date="1538948346076" expanded="true" signature="2071:2953" ph="..." />
|
||||
<marker date="1538948346076" expanded="true" signature="5760:6122" ph="..." />
|
||||
<marker date="1538948346076" expanded="true" signature="5843:6122" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/main_default.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="377">
|
||||
<caret line="24" selection-start-line="24" selection-end-line="24" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/models.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="439">
|
||||
<caret line="70" lean-forward="true" selection-start-line="70" selection-end-line="70" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-second>
|
||||
</splitter>
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="SCSS File" />
|
||||
<option value="Python Script" />
|
||||
<option value="Setup Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
@ -146,12 +262,21 @@
|
||||
<find>url</find>
|
||||
<find>delete_fo</find>
|
||||
<find>folder</find>
|
||||
<find>modal</find>
|
||||
<find>folderEditDialog</find>
|
||||
<find>subscriptionEditDialog_</find>
|
||||
<find>folder_edit_dialog</find>
|
||||
<find>folderEditDialog_</find>
|
||||
<find>treeNode_Edit</find>
|
||||
<find>default_app_config</find>
|
||||
<find>start_synchronization_timer</find>
|
||||
</findStrings>
|
||||
<replaceStrings>
|
||||
<replace>loading</replace>
|
||||
<replace />
|
||||
<replace>subscription</replace>
|
||||
<replace>subscription</replace>
|
||||
<replace>subscriptionEditDialog</replace>
|
||||
<replace>folderEditDialog</replace>
|
||||
</replaceStrings>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
@ -165,14 +290,20 @@
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/main_default.html" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/import/jstree/jstree.min.js" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.scss" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_dialog.html" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/views.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/js/subscription_tree.js" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_dialog.html" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html" />
|
||||
<option value="$PROJECT_DIR$/YtManager/urls.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/views.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/admin.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/__init__.py" />
|
||||
<option value="$PROJECT_DIR$/YtManager/settings.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/apps.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/models.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/youtube.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management.py" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
@ -183,10 +314,9 @@
|
||||
<sorting>DEFINITION_ORDER</sorting>
|
||||
</component>
|
||||
<component name="ProjectFrameBounds" extendedState="6">
|
||||
<option name="x" value="-8" />
|
||||
<option name="y" value="1" />
|
||||
<option name="width" value="1400" />
|
||||
<option name="height" value="1000" />
|
||||
<option name="x" value="1902" />
|
||||
<option name="width" value="1953" />
|
||||
<option name="height" value="2058" />
|
||||
</component>
|
||||
<component name="ProjectView">
|
||||
<navigator proportions="" version="1">
|
||||
@ -210,48 +340,6 @@
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="controls" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="js" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</subPane>
|
||||
@ -262,11 +350,12 @@
|
||||
<component name="PropertiesComponent">
|
||||
<property name="SearchEverywhereHistoryKey" value="	FILE	file://D:/Dev/youtube-channel-manager/YtManagerApp/views.py" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="last_opened_file_path" value="$USER_HOME$/AppData/Roaming/npm/lessc.cmd" />
|
||||
<property name="database.console.LAST_STATE" value="false" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../YoutubeApi-tests/get_playlist_info.py" />
|
||||
<property name="list.type.of.created.stylesheet" value="SCSS" />
|
||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
||||
<property name="settings.editor.selected.configurable" value="preferences.sourceCode" />
|
||||
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
@ -328,9 +417,34 @@
|
||||
<servers />
|
||||
</component>
|
||||
<component name="ToolWindowManager">
|
||||
<frame x="-8" y="-8" width="1936" height="1056" extended-state="6" />
|
||||
<frame x="-6" y="-6" width="1292" height="692" extended-state="6" />
|
||||
<editor active="true" />
|
||||
<layout>
|
||||
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.25970873" />
|
||||
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
|
||||
<window_info id="Favorites" order="2" side_tool="true" />
|
||||
<window_info anchor="bottom" id="Database Console" sideWeight="0.49838188" weight="0.3285968" />
|
||||
<window_info anchor="bottom" id="Message" order="0" />
|
||||
<window_info anchor="bottom" id="Find" order="1" sideWeight="0.4989339" weight="0.329718" />
|
||||
<window_info active="true" anchor="bottom" id="Run" order="2" sideWeight="0.49573562" visible="true" weight="0.3090586" />
|
||||
<window_info anchor="bottom" id="Debug" order="3" sideWeight="0.49946696" weight="0.3991323" />
|
||||
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
|
||||
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
|
||||
<window_info anchor="bottom" id="TODO" order="6" />
|
||||
<window_info anchor="bottom" id="manage.py@youtube-channel-manager" order="7" sideWeight="0.49840087" weight="0.3286334" />
|
||||
<window_info anchor="bottom" id="Docker" order="8" show_stripe_button="false" />
|
||||
<window_info anchor="bottom" id="Database Changes" order="9" />
|
||||
<window_info anchor="bottom" id="Event Log" order="10" sideWeight="0.50161815" side_tool="true" weight="0.5399645" />
|
||||
<window_info anchor="bottom" id="Version Control" order="11" weight="0.329718" />
|
||||
<window_info anchor="bottom" id="Terminal" order="12" sideWeight="0.49733475" weight="0.35791758" />
|
||||
<window_info anchor="bottom" id="Python Console" order="13" sideWeight="0.49840087" weight="0.3286334" />
|
||||
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
|
||||
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
|
||||
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
|
||||
<window_info anchor="right" id="SciView" order="3" />
|
||||
<window_info anchor="right" id="Database" order="4" weight="0.31634304" />
|
||||
</layout>
|
||||
<layout-to-restore>
|
||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.18017058" />
|
||||
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
|
||||
<window_info id="Favorites" order="2" side_tool="true" />
|
||||
@ -353,7 +467,7 @@
|
||||
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
|
||||
<window_info anchor="right" id="SciView" order="3" />
|
||||
<window_info anchor="right" id="Database" order="4" />
|
||||
</layout>
|
||||
</layout-to-restore>
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="1" />
|
||||
@ -362,13 +476,7 @@
|
||||
<option name="myLimit" value="2678400000" />
|
||||
</component>
|
||||
<component name="editorHistoryManager">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.less">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state>
|
||||
<caret column="18" lean-forward="true" selection-start-column="18" selection-end-column="22" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.less" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/import/jstree/jstree.min.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="85">
|
||||
@ -379,13 +487,7 @@
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/_style.css">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="272">
|
||||
<caret line="16" lean-forward="true" selection-start-line="16" selection-end-line="17" selection-end-column="26" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/_style.css" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.scss">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="170">
|
||||
@ -393,68 +495,169 @@
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/main_master_detail.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="51">
|
||||
<caret line="3" lean-forward="true" selection-start-line="3" selection-end-line="3" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/youtube/__init__.py" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_dialog.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="340">
|
||||
<caret line="20" column="29" selection-start-line="20" selection-start-column="29" selection-end-line="20" selection-end-column="29" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../YoutubeApi-tests/get_playlist_info.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="340">
|
||||
<caret line="47" column="22" lean-forward="true" selection-start-line="47" selection-start-column="22" selection-end-line="47" selection-end-column="22" />
|
||||
<folding>
|
||||
<element signature="e#0#43#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="138">
|
||||
<caret line="22" column="45" selection-start-line="22" selection-start-column="45" selection-end-line="22" selection-end-column="45" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="221">
|
||||
<caret line="13" column="72" selection-start-line="13" selection-start-column="72" selection-end-line="13" selection-end-column="72" />
|
||||
<caret line="13" column="33" selection-start-line="13" selection-start-column="33" selection-end-line="13" selection-end-column="33" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_subscriptionfolder">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_subscription">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_channel">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManager/wsgi.py">
|
||||
<provider selected="true" editor-type-id="text-editor" />
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManager/settings.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="-6">
|
||||
<caret line="16" lean-forward="true" selection-start-line="16" selection-end-line="16" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/main_default.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="357">
|
||||
<caret line="26" column="4" lean-forward="true" selection-start-line="26" selection-start-column="4" selection-end-line="26" selection-end-column="4" />
|
||||
<state relative-caret-position="377">
|
||||
<caret line="24" selection-start-line="24" selection-end-line="24" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/apps.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="153">
|
||||
<caret line="9" lean-forward="true" selection-start-line="9" selection-end-line="9" />
|
||||
<folding>
|
||||
<marker date="1538943422238" expanded="true" signature="120:125" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/admin.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="102">
|
||||
<caret line="6" selection-start-line="6" selection-end-line="6" />
|
||||
<folding>
|
||||
<element signature="e#0#32#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="334">
|
||||
<caret line="62" lean-forward="true" selection-start-line="62" selection-end-line="62" />
|
||||
<state relative-caret-position="469">
|
||||
<caret line="87" lean-forward="true" selection-start-line="87" selection-end-line="87" />
|
||||
<folding>
|
||||
<element signature="e#0#35#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management.py">
|
||||
<entry file="file://$PROJECT_DIR$/YtManager/urls.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="504">
|
||||
<caret line="42" column="11" selection-start-line="42" selection-start-column="11" selection-end-line="42" selection-end-column="11" />
|
||||
<state relative-caret-position="289">
|
||||
<caret line="17" selection-start-line="17" selection-end-line="17" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/__init__.py">
|
||||
<provider selected="true" editor-type-id="text-editor" />
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="438">
|
||||
<caret line="338" column="3" selection-start-line="338" selection-start-column="3" selection-end-line="338" selection-end-column="3" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/models.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="439">
|
||||
<caret line="70" lean-forward="true" selection-start-line="70" selection-end-line="70" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_video">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/youtube.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="354">
|
||||
<caret line="206" column="24" selection-start-line="206" selection-start-column="24" selection-end-line="206" selection-end-column="24" />
|
||||
<folding>
|
||||
<marker date="1538766618853" expanded="true" signature="90:91" ph="..." />
|
||||
<marker date="1538766618853" expanded="true" signature="642:651" ph="..." />
|
||||
<element signature="e#0#43#0" expanded="true" />
|
||||
<marker date="1538948346076" expanded="true" signature="2071:2953" ph="..." />
|
||||
<marker date="1538948346076" expanded="true" signature="5760:6122" ph="..." />
|
||||
<marker date="1538948346076" expanded="true" signature="5843:6122" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManager/urls.py">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="272">
|
||||
<caret line="19" lean-forward="true" selection-start-line="19" selection-end-line="19" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="113">
|
||||
<caret line="20" column="28" selection-start-line="20" selection-start-column="28" selection-end-line="20" selection-end-column="28" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/subscription_tree.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1700">
|
||||
<caret line="100" column="186" selection-start-line="100" selection-start-column="186" selection-end-line="100" selection-end-column="186" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/subscription_edit_dialog.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="357">
|
||||
<caret line="21" column="30" lean-forward="true" selection-start-line="21" selection-start-column="30" selection-end-line="21" selection-end-column="30" />
|
||||
<state relative-caret-position="208">
|
||||
<caret line="183" column="47" selection-start-line="183" selection-start-column="47" selection-end-line="183" selection-end-column="47" />
|
||||
<folding>
|
||||
<element signature="e#433#440#1#HTML" expanded="true" />
|
||||
<element signature="e#0#68#0" expanded="true" />
|
||||
<marker date="1538954367974" expanded="true" signature="819:828" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="1597:1603" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="4940:6762" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="5447:5983" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="6067:6076" ph="..." />
|
||||
<marker date="1538954367974" expanded="true" signature="6067:6496" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
|
@ -22,6 +22,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '^zv8@i2h!ko2lo=%ivq(9e#x=%q*i^^)6#4@(juzdx%&0c+9a0'
|
||||
|
||||
YOUTUBE_API_KEY = "AIzaSyBabzE4Bup77WexdLMa9rN9z-wJidEfNX8"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
@ -31,7 +33,7 @@ ALLOWED_HOSTS = []
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'YtManagerApp',
|
||||
'YtManagerApp.apps.YtManagerAppConfig',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
|
@ -24,5 +24,7 @@ urlpatterns = [
|
||||
path('ajax/get_folders', views.ajax_get_folders, name='ajax_get_folders'),
|
||||
path('ajax/edit_folder', views.ajax_edit_folder, name='ajax_edit_folder'),
|
||||
path('ajax/delete_folder/<int:fid>/', views.ajax_delete_folder, name='ajax_delete_folder'),
|
||||
path('ajax/edit_subscription', views.ajax_edit_subscription, name='ajax_edit_subscription'),
|
||||
path('ajax/delete_subscription/<int:sid>/', views.ajax_delete_subscription, name='ajax_delete_subscription'),
|
||||
path(r'', views.index, name='home')
|
||||
]
|
||||
|
@ -3,4 +3,4 @@ from .models import SubscriptionFolder, Subscription, Video
|
||||
|
||||
admin.site.register(SubscriptionFolder)
|
||||
admin.site.register(Subscription)
|
||||
admin.site.register(Video)
|
||||
admin.site.register(Video)
|
||||
|
@ -1,5 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class YtmanagerappConfig(AppConfig):
|
||||
class YtManagerAppConfig(AppConfig):
|
||||
name = 'YtManagerApp'
|
||||
|
||||
def ready(self):
|
||||
from .management import SubscriptionManager
|
||||
SubscriptionManager.start_scheduler()
|
||||
|
@ -1,5 +1,7 @@
|
||||
from .models import SubscriptionFolder, Subscription, Video
|
||||
|
||||
from .models import SubscriptionFolder, Subscription, Video, Channel
|
||||
from .youtube import YoutubeAPI, YoutubeChannelInfo, YoutubePlaylistItem
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
import os
|
||||
|
||||
class FolderManager(object):
|
||||
|
||||
@ -22,7 +24,7 @@ class FolderManager(object):
|
||||
folder.save()
|
||||
|
||||
@staticmethod
|
||||
def __validate(folder):
|
||||
def __validate(folder: SubscriptionFolder):
|
||||
# Make sure folder name is unique in the parent folder
|
||||
for dbFolder in SubscriptionFolder.objects.filter(parent_id=folder.parent_id):
|
||||
if dbFolder.id != folder.id and dbFolder.name == folder.name:
|
||||
@ -42,3 +44,141 @@ class FolderManager(object):
|
||||
def delete(fid: int):
|
||||
folder = SubscriptionFolder.objects.get(id=fid)
|
||||
folder.delete()
|
||||
|
||||
|
||||
class SubscriptionManager(object):
|
||||
__scheduler = BackgroundScheduler()
|
||||
|
||||
@staticmethod
|
||||
def create_or_edit(sid, url, name, parent_id):
|
||||
# Create or edit
|
||||
if sid == '#':
|
||||
sub = Subscription()
|
||||
SubscriptionManager.create(url, parent_id, YoutubeAPI.build_public())
|
||||
else:
|
||||
sub = Subscription.objects.get(id=int(sid))
|
||||
sub.name = name
|
||||
|
||||
if parent_id == '#':
|
||||
sub.parent_folder = None
|
||||
else:
|
||||
sub.parent_folder = SubscriptionFolder.objects.get(id=int(parent_id))
|
||||
|
||||
sub.save()
|
||||
|
||||
@staticmethod
|
||||
def create(url, parent_id, yt_api: YoutubeAPI):
|
||||
sub = Subscription()
|
||||
# Set parent
|
||||
if parent_id == '#':
|
||||
sub.parent_folder = None
|
||||
else:
|
||||
sub.parent_folder = SubscriptionFolder.objects.get(id=int(parent_id))
|
||||
|
||||
# Pull information about the channel and playlist
|
||||
url_type, url_id = yt_api.parse_channel_url(url)
|
||||
|
||||
if url_type == 'playlist_id':
|
||||
info_playlist = yt_api.get_playlist_info(url_id)
|
||||
channel = SubscriptionManager.__get_or_create_channel('channel_id', info_playlist.getChannelId(), yt_api)
|
||||
sub.name = info_playlist.getTitle()
|
||||
sub.playlist_id = info_playlist.getId()
|
||||
sub.description = info_playlist.getDescription()
|
||||
sub.channel = channel
|
||||
sub.icon_default = info_playlist.getDefaultThumbnailUrl()
|
||||
sub.icon_best = info_playlist.getBestThumbnailUrl()
|
||||
|
||||
else:
|
||||
channel = SubscriptionManager.__get_or_create_channel(url_type, url_id, yt_api)
|
||||
# No point in getting the 'uploads' playlist info
|
||||
sub.name = channel.name
|
||||
sub.playlist_id = channel.upload_playlist_id
|
||||
sub.description = channel.description
|
||||
sub.channel = channel
|
||||
sub.icon_default = channel.icon_default
|
||||
sub.icon_best = channel.icon_best
|
||||
|
||||
sub.save()
|
||||
|
||||
@staticmethod
|
||||
def __get_or_create_channel(url_type, url_id, yt_api: YoutubeAPI):
|
||||
|
||||
channel: Channel = None
|
||||
info_channel: YoutubeChannelInfo = None
|
||||
|
||||
if url_type == 'user':
|
||||
channel = Channel.find_by_username(url_id)
|
||||
if not channel:
|
||||
info_channel = yt_api.get_channel_info_by_username(url_id)
|
||||
channel = Channel.find_by_channel_id(info_channel.getId())
|
||||
|
||||
elif url_type == 'channel_id':
|
||||
channel = Channel.find_by_channel_id(url_id)
|
||||
if not channel:
|
||||
info_channel = yt_api.get_channel_info(url_id)
|
||||
|
||||
elif url_type == 'channel_custom':
|
||||
channel = Channel.find_by_custom_url(url_id)
|
||||
if not channel:
|
||||
found_channel_id = yt_api.search_channel(url_id)
|
||||
channel = Channel.find_by_channel_id(found_channel_id)
|
||||
if not channel:
|
||||
info_channel = yt_api.get_channel_info(found_channel_id)
|
||||
|
||||
# Store information about the channel
|
||||
if info_channel:
|
||||
if not channel:
|
||||
channel = Channel()
|
||||
if url_type == 'user':
|
||||
channel.username = url_id
|
||||
SubscriptionManager.__update_channel(channel, info_channel)
|
||||
|
||||
return channel
|
||||
|
||||
@staticmethod
|
||||
def __update_channel(channel: Channel, yt_info: YoutubeChannelInfo):
|
||||
channel.channel_id = yt_info.getId()
|
||||
channel.custom_url = yt_info.getCustomUrl()
|
||||
channel.name = yt_info.getTitle()
|
||||
channel.description = yt_info.getDescription()
|
||||
channel.icon_default = yt_info.getDefaultThumbnailUrl()
|
||||
channel.icon_best = yt_info.getBestThumbnailUrl()
|
||||
channel.upload_playlist_id = yt_info.getUploadsPlaylist()
|
||||
channel.save()
|
||||
|
||||
@staticmethod
|
||||
def __create_video(yt_video: YoutubePlaylistItem, subscription: Subscription):
|
||||
video = Video()
|
||||
video.video_id = yt_video.getVideoId()
|
||||
video.name = yt_video.getTitle()
|
||||
video.description = yt_video.getDescription()
|
||||
video.watched = False
|
||||
video.downloaded_path = None
|
||||
video.subscription = subscription
|
||||
video.playlist_index = yt_video.getPlaylistIndex()
|
||||
video.publish_date = yt_video.getPublishDate()
|
||||
video.icon_default = yt_video.getDefaultThumbnailUrl()
|
||||
video.icon_best = yt_video.getBestThumbnailUrl()
|
||||
video.save()
|
||||
|
||||
@staticmethod
|
||||
def __synchronize(subscription: Subscription, yt_api: YoutubeAPI):
|
||||
# Get list of videos
|
||||
for video in yt_api.list_playlist_videos(subscription.playlist_id):
|
||||
results = Video.objects.filter(video_id=video.getVideoId(), subscription=subscription)
|
||||
if len(results) == 0:
|
||||
print('New video for subscription "', subscription, '": ', video.getVideoId(), video.getTitle())
|
||||
SubscriptionManager.__create_video(video, subscription)
|
||||
|
||||
@staticmethod
|
||||
def __synchronize_all():
|
||||
print("Running scheduled synchronization... ")
|
||||
yt_api = YoutubeAPI.build_public()
|
||||
for subscription in Subscription.objects.all():
|
||||
SubscriptionManager.__synchronize(subscription, yt_api)
|
||||
|
||||
@staticmethod
|
||||
def start_scheduler():
|
||||
SubscriptionManager.__scheduler.add_job(SubscriptionManager.__synchronize_all, 'cron',
|
||||
hour='*', minute=44, max_instances=1)
|
||||
SubscriptionManager.__scheduler.start()
|
||||
|
57
YtManagerApp/migrations/0005_auto_20181007_2015.py
Normal file
57
YtManagerApp/migrations/0005_auto_20181007_2015.py
Normal file
@ -0,0 +1,57 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-07 17:15
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0004_auto_20181005_1626'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Channel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('channel_id', models.TextField(unique=True)),
|
||||
('username', models.TextField(null=True, unique=True)),
|
||||
('custom_url', models.TextField(null=True, unique=True)),
|
||||
('name', models.TextField()),
|
||||
('description', models.TextField()),
|
||||
('icon_default', models.TextField()),
|
||||
('icon_best', models.TextField()),
|
||||
('upload_playlist_id', models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='subscription',
|
||||
old_name='url',
|
||||
new_name='playlist_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='description',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='icon_best',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='icon_default',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='channel',
|
||||
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.Channel'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
49
YtManagerApp/migrations/0006_auto_20181008_0037.py
Normal file
49
YtManagerApp/migrations/0006_auto_20181008_0037.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-07 21:37
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0005_auto_20181007_2015'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='video',
|
||||
old_name='ytid',
|
||||
new_name='video_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='description',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='icon_best',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='icon_default',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='playlist_index',
|
||||
field=models.IntegerField(default=0),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='publish_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,5 +1,6 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class SubscriptionFolder(models.Model):
|
||||
name = models.TextField(null=False)
|
||||
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
|
||||
@ -8,21 +9,65 @@ class SubscriptionFolder(models.Model):
|
||||
return self.name
|
||||
|
||||
|
||||
class Channel(models.Model):
|
||||
channel_id = models.TextField(null=False, unique=True)
|
||||
username = models.TextField(null=True, unique=True)
|
||||
custom_url = models.TextField(null=True, unique=True)
|
||||
name = models.TextField()
|
||||
description = models.TextField()
|
||||
icon_default = models.TextField()
|
||||
icon_best = models.TextField()
|
||||
upload_playlist_id = models.TextField()
|
||||
|
||||
@staticmethod
|
||||
def find_by_channel_id(channel_id):
|
||||
result = Channel.objects.filter(channel_id=channel_id)
|
||||
if len(result) > 0:
|
||||
return result.first()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def find_by_username(username):
|
||||
result = Channel.objects.filter(username=username)
|
||||
if len(result) > 0:
|
||||
return result.first()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def find_by_custom_url(custom_url):
|
||||
result = Channel.objects.filter(custom_url=custom_url)
|
||||
if len(result) > 0:
|
||||
return result.first()
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
name = models.TextField(null=False)
|
||||
parent_folder = models.ForeignKey(SubscriptionFolder, on_delete=models.SET_NULL, null=True, blank=True)
|
||||
url = models.TextField(null=False, unique=True)
|
||||
|
||||
playlist_id = models.TextField(null=False, unique=True)
|
||||
description = models.TextField()
|
||||
channel = models.ForeignKey(Channel, on_delete=models.CASCADE)
|
||||
icon_default = models.TextField()
|
||||
icon_best = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Video(models.Model):
|
||||
video_id = models.TextField(null=False)
|
||||
name = models.TextField(null=False)
|
||||
ytid = models.TextField(null=False)
|
||||
downloaded_path = models.TextField(null=True, blank=True)
|
||||
description = models.TextField()
|
||||
watched = models.BooleanField(default=False, null=False)
|
||||
downloaded_path = models.TextField(null=True, blank=True)
|
||||
subscription = models.ForeignKey(Subscription, on_delete=models.CASCADE)
|
||||
playlist_index = models.IntegerField(null=False)
|
||||
publish_date = models.DateTimeField(null=False)
|
||||
icon_default = models.TextField()
|
||||
icon_best = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
@ -1,36 +1,37 @@
|
||||
<div id="folder_edit_dialog" class="modal" tabindex="-1" role="dialog">
|
||||
<div id="folderEditDialog" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 id="folder_edit_dialog_title" class="modal-title">Edit folder</h5>
|
||||
<h5 id="folderEditDialog_Title" class="modal-title">Edit folder</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="folder_edit_dialog_loading" class="modal-body">
|
||||
<div id="folderEditDialog_Loading" class="modal-body">
|
||||
<div class="loading-dual-ring"></div>
|
||||
<div id="folder_edit_dialog_error"></div>
|
||||
</div>
|
||||
<form id="folder_edit_dialog_form" action="{% url 'ajax_edit_folder' %}" method="post">
|
||||
<div id="folderEditDialog_Error">
|
||||
</div>
|
||||
<form id="folderEditDialog_Form" action="{% url 'ajax_edit_folder' %}" method="post">
|
||||
<div class="modal-body">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="folder_edit_dialog_id" name="id" value="#">
|
||||
<input type="hidden" id="folderEditDialog_Id" name="id" value="#">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3" for="folder_edit_dialog_name">Name</label>
|
||||
<label class="col-sm-3" for="folderEditDialog_Name">Name</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="folder_edit_dialog_name" name="name" placeholder="Folder name">
|
||||
<input type="text" class="form-control" id="folderEditDialog_Name" name="name" placeholder="Folder name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3" for="folder_edit_dialog_parent">Parent folder</label>
|
||||
<label class="col-sm-3" for="folderEditDialog_Parent">Parent folder</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control" id="folder_edit_dialog_parent" name="parent">
|
||||
<select class="form-control" id="folderEditDialog_Parent" name="parent">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="folder_edit_dialog_submit" type="submit" class="btn btn-primary">Submit</button>
|
||||
<button id="folderEditDialog_Submit" type="submit" class="btn btn-primary">Submit</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,36 +1,42 @@
|
||||
<div id="subscription_edit_dialog" class="modal" tabindex="-1" role="dialog">
|
||||
<div id="subscriptionEditDialog" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 id="subscription_edit_dialog_title" class="modal-title">Edit subscription</h5>
|
||||
<h5 id="subscriptionEditDialog_Title" class="modal-title">Edit subscription</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="subscription_edit_dialog_loading" class="modal-body">
|
||||
<div id="subscriptionEditDialog_Loading" class="modal-body">
|
||||
<div class="loading-dual-ring"></div>
|
||||
<div id="subscription_edit_dialog_error"></div>
|
||||
</div>
|
||||
<form id="subscription_edit_dialog_form" action="{% url 'ajax_edit_subscription' %}" method="post">
|
||||
<div id="subscriptionEditDialog_Error"></div>
|
||||
<form id="subscriptionEditDialog_Form" action="{% url 'ajax_edit_subscription' %}" method="post">
|
||||
<div class="modal-body">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="subscription_edit_dialog_id" name="id" value="#">
|
||||
<input type="hidden" id="subscriptionEditDialog_Id" name="id" value="#">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3" for="subscription_edit_dialog_url">Link:</label>
|
||||
<label class="col-sm-3" for="subscriptionEditDialog_Url">Link:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="subscription_edit_dialog_name" name="name" placeholder="subscription name">
|
||||
<input type="text" class="form-control" id="subscriptionEditDialog_Url" name="url" placeholder="Subscription URL (playlist, channel)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3" for="subscription_edit_dialog_parent">Parent subscription</label>
|
||||
<label class="col-sm-3" for="subscriptionEditDialog_Name">Name:</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control" id="subscription_edit_dialog_parent" name="parent">
|
||||
<input type="text" class="form-control" id="subscriptionEditDialog_Name" name="name" placeholder="Subscription name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3" for="subscriptionEditDialog_Parent">Parent subscription</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control" id="subscriptionEditDialog_Parent" name="parent">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="subscription_edit_dialog_submit" type="submit" class="btn btn-primary">Submit</button>
|
||||
<button id="subscriptionEditDialog_Submit" type="submit" class="btn btn-primary">Submit</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -12,6 +12,7 @@
|
||||
</script>
|
||||
|
||||
{% include 'YtManagerApp/controls/folder_edit_dialog.html' %}
|
||||
{% include 'YtManagerApp/controls/subscription_edit_dialog.html' %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -19,7 +20,7 @@
|
||||
|
||||
<div class="btn-toolbar" role="toolbar" aria-label="Subscriptions toolbar">
|
||||
<div class="btn-group btn-group-sm mr-2" role="group">
|
||||
<button type="button" class="btn btn-secondary" >
|
||||
<button id="btn_create_sub" type="button" class="btn btn-secondary" >
|
||||
<i class="material-icons" aria-hidden="true">add</i>
|
||||
</button>
|
||||
<button id="btn_create_folder" type="button" class="btn btn-secondary">
|
||||
|
@ -1,79 +1,226 @@
|
||||
function folderEditDialog_Show(isNew, editNode)
|
||||
{
|
||||
let dialog = $("#folder_edit_dialog");
|
||||
dialog.find('#folder_edit_dialog_title').text(isNew ? "New folder" : "Edit folder");
|
||||
dialog.find("#folder_edit_dialog_loading").show();
|
||||
dialog.find("#folder_edit_dialog_error").hide();
|
||||
dialog.find("#folder_edit_dialog_form").hide();
|
||||
dialog.modal();
|
||||
class Dialog {
|
||||
constructor(modalId) {
|
||||
this.modal = $(modalId);
|
||||
this.title = $(modalId + "_Title");
|
||||
this.form = $(modalId + "_Form");
|
||||
this.error = $(modalId + "_Error");
|
||||
this.loading = $(modalId + "_Loading");
|
||||
this.btnSubmit = $(modalId + "_Submit");
|
||||
this.setState('normal');
|
||||
}
|
||||
|
||||
$.get("{% url 'ajax_get_folders' %}")
|
||||
.done(function(folders)
|
||||
{
|
||||
// Populate list of folders
|
||||
let selParent = dialog.find("#folder_edit_dialog_parent");
|
||||
selParent.empty();
|
||||
selParent.append(new Option('(None)', '#'));
|
||||
setTitle(value) {
|
||||
this.title.text(value);
|
||||
}
|
||||
|
||||
let parentId = null;
|
||||
if (!isNew) {
|
||||
parentId = editNode.parent.replace('folder', '');
|
||||
}
|
||||
setState(state) {
|
||||
if (state === 'loading') {
|
||||
this.loading.show();
|
||||
this.error.hide();
|
||||
this.form.hide();
|
||||
}
|
||||
if (state === 'error') {
|
||||
this.loading.hide();
|
||||
this.error.show();
|
||||
this.form.hide();
|
||||
}
|
||||
if (state === 'normal') {
|
||||
this.loading.hide();
|
||||
this.error.hide();
|
||||
this.form.show();
|
||||
}
|
||||
}
|
||||
|
||||
for (let folder of folders)
|
||||
{
|
||||
let o = new Option(folder.text, folder.id);
|
||||
if (!isNew && folder.id.toString() === parentId.toString())
|
||||
o.selected = true;
|
||||
setError(text) {
|
||||
this.error.text(text);
|
||||
}
|
||||
|
||||
selParent.append(o);
|
||||
}
|
||||
showModal() {
|
||||
this.modal.modal();
|
||||
}
|
||||
|
||||
// Show form
|
||||
dialog.find("#folder_edit_dialog_loading").hide();
|
||||
dialog.find("#folder_edit_dialog_form").show();
|
||||
dialog.find("#folder_edit_dialog_submit").text(isNew ? "Create" : "Save");
|
||||
hideModal() {
|
||||
this.modal.modal('hide');
|
||||
}
|
||||
}
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
dialog.find("#folder_edit_dialog_id").val('#');
|
||||
dialog.find("#folder_edit_dialog_name").val('');
|
||||
}
|
||||
if (!isNew)
|
||||
{
|
||||
idTrimmed = editNode.id.replace('folder', '');
|
||||
dialog.find("#folder_edit_dialog_id").val(idTrimmed);
|
||||
dialog.find("#folder_edit_dialog_name").val(editNode.text);
|
||||
}
|
||||
class FolderEditDialog extends Dialog {
|
||||
|
||||
constructor (modalId) {
|
||||
super(modalId);
|
||||
this.field_Id = $(modalId + "_Id");
|
||||
this.field_Name = $(modalId + "_Name");
|
||||
this.field_Parent = $(modalId + "_Parent");
|
||||
|
||||
let pThis = this;
|
||||
this.form.submit(function(e) {
|
||||
pThis.submit(e);
|
||||
})
|
||||
.fail(function() {
|
||||
let msgError = dialog.find("#folder_edit_dialog_error");
|
||||
msgError.show();
|
||||
msgError.text("An error occurred!");
|
||||
});
|
||||
}
|
||||
|
||||
setParentFolderOptions(folders, selectedId)
|
||||
{
|
||||
// Populate list of folders
|
||||
this.field_Parent.empty();
|
||||
this.field_Parent.append(new Option('(None)', '#'));
|
||||
|
||||
for (let folder of folders)
|
||||
{
|
||||
let o = new Option(folder.text, folder.id);
|
||||
if (selectedId != null && folder.id.toString() === selectedId.toString())
|
||||
o.selected = true;
|
||||
|
||||
this.field_Parent.append(o);
|
||||
}
|
||||
}
|
||||
|
||||
show (isNew, editNode) {
|
||||
let pThis = this;
|
||||
this.setTitle(isNew ? "New folder" : "Edit folder");
|
||||
this.setState('loading');
|
||||
this.showModal();
|
||||
|
||||
$.get("{% url 'ajax_get_folders' %}")
|
||||
.done(function(folders)
|
||||
{
|
||||
let parentId = null;
|
||||
if (!isNew) {
|
||||
parentId = editNode.parent.replace('folder', '');
|
||||
}
|
||||
|
||||
pThis.setParentFolderOptions(folders, parentId);
|
||||
pThis.setState('normal');
|
||||
pThis.btnSubmit.text(isNew ? "Create" : "Save");
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
pThis.field_Id.val('#');
|
||||
pThis.field_Name.val('');
|
||||
}
|
||||
if (!isNew)
|
||||
{
|
||||
let idTrimmed = editNode.id.replace('folder', '');
|
||||
pThis.field_Id.val(idTrimmed);
|
||||
pThis.field_Name.val(editNode.text);
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
pThis.setState('error');
|
||||
pThis.setError('An error occurred!');
|
||||
});
|
||||
}
|
||||
|
||||
showNew() {
|
||||
this.show(true, null);
|
||||
}
|
||||
|
||||
showEdit(editNode) {
|
||||
this.show(false, editNode);
|
||||
}
|
||||
|
||||
submit(e) {
|
||||
let url = this.form.attr('action');
|
||||
|
||||
$.post(url, this.form.serialize())
|
||||
.done(tree_Refresh);
|
||||
|
||||
this.hideModal();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function folderEditDialog_ShowNew()
|
||||
{
|
||||
folderEditDialog_Show(true, null);
|
||||
class SubscriptionEditDialog extends Dialog {
|
||||
|
||||
constructor (modalId) {
|
||||
super(modalId);
|
||||
this.field_Id = $(modalId + "_Id");
|
||||
this.field_Url = $(modalId + "_Url");
|
||||
this.field_Name = $(modalId + "_Name");
|
||||
this.field_Parent = $(modalId + "_Parent");
|
||||
|
||||
let pThis = this;
|
||||
this.form.submit(function(e) {
|
||||
pThis.submit(e);
|
||||
})
|
||||
}
|
||||
|
||||
setParentFolderOptions(folders, selectedId)
|
||||
{
|
||||
// Populate list of folders
|
||||
this.field_Parent.empty();
|
||||
this.field_Parent.append(new Option('(None)', '#'));
|
||||
|
||||
for (let folder of folders)
|
||||
{
|
||||
let o = new Option(folder.text, folder.id);
|
||||
if (selectedId != null && folder.id.toString() === selectedId.toString())
|
||||
o.selected = true;
|
||||
|
||||
this.field_Parent.append(o);
|
||||
}
|
||||
}
|
||||
|
||||
show (isNew, editNode) {
|
||||
let pThis = this;
|
||||
this.setTitle(isNew ? "New subscription" : "Edit subscription");
|
||||
this.setState('loading');
|
||||
this.showModal();
|
||||
|
||||
$.get("{% url 'ajax_get_folders' %}")
|
||||
.done(function(folders)
|
||||
{
|
||||
let parentId = null;
|
||||
if (!isNew) {
|
||||
parentId = editNode.parent.replace('folder', '');
|
||||
}
|
||||
|
||||
pThis.setParentFolderOptions(folders, parentId);
|
||||
pThis.setState('normal');
|
||||
pThis.btnSubmit.text(isNew ? "Create" : "Save");
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
pThis.field_Id.val('#');
|
||||
pThis.field_Url.show();
|
||||
pThis.field_Url.val('');
|
||||
pThis.field_Name.hide();
|
||||
pThis.field_Name.val('');
|
||||
}
|
||||
if (!isNew)
|
||||
{
|
||||
let idTrimmed = editNode.id.replace('sub', '');
|
||||
pThis.field_Id.val(idTrimmed);
|
||||
pThis.field_Url.hide();
|
||||
pThis.field_Url.val('');
|
||||
pThis.field_Name.show();
|
||||
pThis.field_Name.val(editNode.text);
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
pThis.setState('error');
|
||||
pThis.setError('An error occurred!');
|
||||
});
|
||||
}
|
||||
|
||||
showNew() {
|
||||
this.show(true, null);
|
||||
}
|
||||
|
||||
showEdit(editNode) {
|
||||
this.show(false, editNode);
|
||||
}
|
||||
|
||||
submit(e) {
|
||||
let url = this.form.attr('action');
|
||||
|
||||
$.post(url, this.form.serialize())
|
||||
.done(tree_Refresh);
|
||||
|
||||
this.hideModal();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function folderEditDialog_Close()
|
||||
{
|
||||
$("#folder_edit_dialog").modal('hide');
|
||||
}
|
||||
|
||||
function folderEditDialog_Submit(e)
|
||||
{
|
||||
let form = $(this);
|
||||
let url = form.attr('action');
|
||||
|
||||
$.post(url, form.serialize())
|
||||
.done(tree_Refresh);
|
||||
|
||||
folderEditDialog_Close();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function treeNode_Edit()
|
||||
{
|
||||
@ -82,10 +229,10 @@ function treeNode_Edit()
|
||||
{
|
||||
let node = selectedNodes[0];
|
||||
if (node.type === 'folder') {
|
||||
folderEditDialog_Show(false, node);
|
||||
folderEditDialog.showEdit(node);
|
||||
}
|
||||
else {
|
||||
// TODO...
|
||||
subscriptionEditDialog.showEdit(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,7 +253,13 @@ function treeNode_Delete()
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO...
|
||||
let subId = node.id.toString().replace('sub', '');
|
||||
if (confirm('Are you sure you want to delete subscription "' + node.text + '"?'))
|
||||
{
|
||||
$.post("{% url 'ajax_delete_subscription' 99999 %}".replace('99999', subId), {
|
||||
csrfmiddlewaretoken: '{{ csrf_token }}'
|
||||
}).done(tree_Refresh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,12 +316,24 @@ function tree_OnSelectionChanged(e, data)
|
||||
node = data.instance.get_selected(true)[0];
|
||||
}
|
||||
|
||||
$(document).ready(function ()
|
||||
///
|
||||
/// Globals
|
||||
///
|
||||
let folderEditDialog = null;
|
||||
let subscriptionEditDialog = null;
|
||||
|
||||
///
|
||||
/// Initialization
|
||||
///
|
||||
$(document).ready(function ()
|
||||
{
|
||||
tree_Initialize();
|
||||
$("#btn_create_folder").on("click", folderEditDialog_ShowNew);
|
||||
|
||||
folderEditDialog = new FolderEditDialog('#folderEditDialog');
|
||||
subscriptionEditDialog = new SubscriptionEditDialog('#subscriptionEditDialog');
|
||||
|
||||
$("#btn_create_sub").on("click", function () { subscriptionEditDialog.showNew(); });
|
||||
$("#btn_create_folder").on("click", function () { folderEditDialog.showNew(); });
|
||||
$("#btn_edit_node").on("click", treeNode_Edit);
|
||||
$("#btn_delete_node").on("click", treeNode_Delete);
|
||||
|
||||
$("#folder_edit_dialog_form").submit(folderEditDialog_Submit);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse, HttpRequest, JsonResponse
|
||||
from .models import SubscriptionFolder, Subscription
|
||||
from .management import FolderManager
|
||||
from .management import FolderManager, SubscriptionManager
|
||||
|
||||
|
||||
def get_children_recurse(parent_id):
|
||||
@ -66,6 +66,22 @@ def ajax_delete_folder(request: HttpRequest, fid):
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
def ajax_edit_subscription(request: HttpRequest):
|
||||
if request.method == 'POST':
|
||||
sid = request.POST['id']
|
||||
name = request.POST['name']
|
||||
url = request.POST['url']
|
||||
parent_id = request.POST['parent']
|
||||
SubscriptionManager.create_or_edit(sid, url, name, parent_id)
|
||||
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
def ajax_delete_subscription(request: HttpRequest, sid):
|
||||
SubscriptionManager.delete(sid)
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
def index(request: HttpRequest):
|
||||
context = {}
|
||||
return render(request, 'YtManagerApp/index.html', context)
|
||||
|
213
YtManagerApp/youtube.py
Normal file
213
YtManagerApp/youtube.py
Normal file
@ -0,0 +1,213 @@
|
||||
from googleapiclient.discovery import build
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from django.conf import settings
|
||||
import re
|
||||
|
||||
API_SERVICE_NAME = 'youtube'
|
||||
API_VERSION = 'v3'
|
||||
|
||||
|
||||
class YoutubeChannelInfo(object):
|
||||
def __init__(self, result_dict):
|
||||
self.__id = result_dict['id']
|
||||
self.__snippet = result_dict['snippet']
|
||||
self.__contentDetails = result_dict['contentDetails']
|
||||
|
||||
def getId(self):
|
||||
return self.__id
|
||||
|
||||
def getTitle(self):
|
||||
return self.__snippet['title']
|
||||
|
||||
def getDescription(self):
|
||||
return self.__snippet['description']
|
||||
|
||||
def getCustomUrl(self):
|
||||
return self.__snippet['customUrl']
|
||||
|
||||
def getDefaultThumbnailUrl(self):
|
||||
return self.__snippet['thumbnails']['default']['url']
|
||||
|
||||
def getBestThumbnailUrl(self):
|
||||
best_url = None
|
||||
best_res = 0
|
||||
for _, thumb in self.__snippet['thumbnails'].items():
|
||||
res = thumb['width'] * thumb['height']
|
||||
if res > best_res:
|
||||
best_res = res
|
||||
best_url = thumb['url']
|
||||
return best_url
|
||||
|
||||
def getUploadsPlaylist(self):
|
||||
return self.__contentDetails['relatedPlaylists']['uploads']
|
||||
|
||||
|
||||
class YoutubePlaylistInfo(object):
|
||||
def __init__(self, result_dict):
|
||||
self.__id = result_dict['id']
|
||||
self.__snippet = result_dict['snippet']
|
||||
|
||||
def getId(self):
|
||||
return self.__id
|
||||
|
||||
def getChannelId(self):
|
||||
return self.__snippet['channelId']
|
||||
|
||||
def getTitle(self):
|
||||
return self.__snippet['title']
|
||||
|
||||
def getDescription(self):
|
||||
return self.__snippet['description']
|
||||
|
||||
def getDefaultThumbnailUrl(self):
|
||||
return self.__snippet['thumbnails']['default']['url']
|
||||
|
||||
def getBestThumbnailUrl(self):
|
||||
best_url = None
|
||||
best_res = 0
|
||||
for _, thumb in self.__snippet['thumbnails'].items():
|
||||
res = thumb['width'] * thumb['height']
|
||||
if res > best_res:
|
||||
best_res = res
|
||||
best_url = thumb['url']
|
||||
return best_url
|
||||
|
||||
|
||||
class YoutubePlaylistItem(object):
|
||||
def __init__(self, result_dict):
|
||||
self.__snippet = result_dict['snippet']
|
||||
|
||||
def getVideoId(self):
|
||||
return self.__snippet['resourceId']['videoId']
|
||||
|
||||
def getPublishDate(self):
|
||||
return self.__snippet['publishedAt']
|
||||
|
||||
def getTitle(self):
|
||||
return self.__snippet['title']
|
||||
|
||||
def getDescription(self):
|
||||
return self.__snippet['description']
|
||||
|
||||
def getDefaultThumbnailUrl(self):
|
||||
return self.__snippet['thumbnails']['default']['url']
|
||||
|
||||
def getBestThumbnailUrl(self):
|
||||
best_url = None
|
||||
best_res = 0
|
||||
for _, thumb in self.__snippet['thumbnails'].items():
|
||||
res = thumb['width'] * thumb['height']
|
||||
if res > best_res:
|
||||
best_res = res
|
||||
best_url = thumb['url']
|
||||
return best_url
|
||||
|
||||
def getPlaylistIndex(self):
|
||||
return self.__snippet['position']
|
||||
|
||||
|
||||
class YoutubeAPI(object):
|
||||
def __init__(self, service):
|
||||
self.service = service
|
||||
|
||||
@staticmethod
|
||||
def build_public() -> 'YoutubeAPI':
|
||||
service = build(API_SERVICE_NAME, API_VERSION, developerKey=settings.YOUTUBE_API_KEY)
|
||||
return YoutubeAPI(service)
|
||||
|
||||
@staticmethod
|
||||
def parse_channel_url(url):
|
||||
"""
|
||||
Parses given channel url, returns a tuple of the form (type, value), where type can be one of:
|
||||
* channel_id
|
||||
* channel_custom
|
||||
* user
|
||||
* playlist_id
|
||||
:param url: URL to parse
|
||||
:return: (type, value) tuple
|
||||
"""
|
||||
match = re.search(r'youtube\.com/.*[&?]list=([^?&/]+)', url)
|
||||
if match:
|
||||
return 'playlist_id', match.group(1)
|
||||
|
||||
match = re.search(r'youtube\.com/user/([^?&/]+)', url)
|
||||
if match:
|
||||
return 'user', match.group(1)
|
||||
|
||||
match = re.search(r'youtube\.com/channel/([^?&/]+)', url)
|
||||
if match:
|
||||
return 'channel_id', match.group(1)
|
||||
|
||||
match = re.search(r'youtube\.com/(?:c/)?([^?&/]+)', url)
|
||||
if match:
|
||||
return 'channel_custom', match.group(1)
|
||||
|
||||
raise Exception('Unrecognized URL format!')
|
||||
|
||||
def get_playlist_info(self, list_id) -> YoutubePlaylistInfo:
|
||||
result = self.service.playlists()\
|
||||
.list(part='snippet', id=list_id)\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise Exception("Invalid playlist ID.")
|
||||
|
||||
return YoutubePlaylistInfo(result['items'][0])
|
||||
|
||||
def get_channel_info_by_username(self, user) -> YoutubeChannelInfo:
|
||||
result = self.service.channels()\
|
||||
.list(part='snippet,contentDetails', forUsername=user)\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise Exception('Invalid user.')
|
||||
|
||||
return YoutubeChannelInfo(result['items'][0])
|
||||
|
||||
def get_channel_info(self, channel_id) -> YoutubeChannelInfo:
|
||||
result = self.service.channels()\
|
||||
.list(part='snippet,contentDetails', id=channel_id)\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise Exception('Invalid channel ID.')
|
||||
|
||||
return YoutubeChannelInfo(result['items'][0])
|
||||
|
||||
def search_channel(self, custom) -> str:
|
||||
result = self.service.search()\
|
||||
.list(part='id', q=custom, type='channel')\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise Exception('Could not find channel!')
|
||||
|
||||
channel_result = result['items'][0]
|
||||
return channel_result['id']['channelId']
|
||||
|
||||
def list_playlist_videos(self, playlist_id):
|
||||
kwargs = {
|
||||
"part": "snippet",
|
||||
"maxResults": 50,
|
||||
"playlistId": playlist_id
|
||||
}
|
||||
last_page = False
|
||||
|
||||
while not last_page:
|
||||
result = self.service.playlistItems()\
|
||||
.list(**kwargs)\
|
||||
.execute()
|
||||
|
||||
for item in result['items']:
|
||||
yield YoutubePlaylistItem(item)
|
||||
|
||||
if 'nextPageToken' in result:
|
||||
kwargs['pageToken'] = result['nextPageToken']
|
||||
else:
|
||||
last_page = True
|
||||
|
||||
# @staticmethod
|
||||
# def build_oauth() -> 'YoutubeAPI':
|
||||
# flow =
|
||||
# credentials =
|
||||
# service = build(API_SERVICE_NAME, API_VERSION, credentials)
|
Loading…
Reference in New Issue
Block a user